add shadows. they even take the same shape as the window. hee!
[dana/dcompmgr.git] / render.c
index fbc4070..f4dd62c 100644 (file)
--- a/render.c
+++ b/render.c
@@ -1,9 +1,12 @@
+#include "efence.h"
+
 #include "render.h"
 #include "screen.h"
 #include "window.h"
 #include "display.h"
 #include "list.h"
 #include <stdio.h>
+#include <assert.h>
 #include <stdlib.h>
 #include <xcb/render.h>
 
@@ -11,30 +14,47 @@ static int plugin_id;
 
 typedef struct {
     void (*screen_paint)(d_screen_t *sc);
+    void (*screen_root_pixmap_changed)(d_screen_t *sc);
     void (*window_show)(d_window_t *w);
     void (*window_hide)(d_window_t *w);
     void (*window_resize)(d_window_t *w);
 
+    xcb_render_pictformat_t root_format;
+    xcb_render_pictformat_t argb32_format;
     xcb_render_query_pict_formats_reply_t *pict_formats;
     xcb_render_picture_t overlay_picture;
     xcb_render_picture_t overlay_buffer;
+    xcb_render_picture_t root_picture;
     xcb_render_picture_t solid_bg;
+
+    xcb_xfixes_region_t all_region;
+    xcb_xfixes_region_t paint_region;
+    xcb_xfixes_region_t shadow_region;
+
+    double shadowalpha;
+    int xshadowoff;
+    int yshadowoff;
 } data_t;
 
 typedef struct {
     xcb_render_picture_t picture;
-    xcb_void_cookie_t    ck_picture;
+    xcb_render_picture_t shadow_picture;
+    xcb_xfixes_region_t paint_clip;
 } window_data_t;
 
-static void render_screen_paint(d_screen_t *sc);
+static void render_paint(d_screen_t *sc);
+static void render_root_pixmap_changed(d_screen_t *sc);
 static void paint_root(d_screen_t *sc, data_t *d);
-static void paint_window(d_window_t *window, data_t *d);
-static void render_update_picture(d_window_t *w, data_t *d, window_data_t *wd,
-                                  gboolean children);
+static void paint_window(d_window_t *window, data_t *d, window_data_t *wd,
+                         gboolean opaque);
+static void paint_shadow(d_window_t *w, data_t *d, window_data_t *wd);
+static void render_update_picture(d_window_t *w, data_t *d, window_data_t *wd);
+static void render_update_root_picture(d_screen_t *sc, data_t *d);
 static void render_free_picture(d_window_t *w, window_data_t *wd);
 static xcb_render_pictformat_t find_visual_format(data_t *d,
                                                   xcb_visualid_t visual);
-static xcb_render_picture_t solid_picture(d_screen_t *sc,
+static xcb_render_pictformat_t find_argb32_format(data_t *d);
+static xcb_render_picture_t solid_picture(data_t *d, d_screen_t *sc,
                                           double a, double r,
                                           double g, double b);
 
@@ -46,19 +66,21 @@ void
 render_init(d_screen_t *sc, int id)
 {
     xcb_render_query_pict_formats_cookie_t ck;
-    xcb_render_pictformat_t format;
     xcb_pixmap_t px;
+    xcb_rectangle_t rect;
 
     plugin_id = id;
 
     data_t *d = malloc(sizeof(data_t));
     d->screen_paint = sc->screen_paint;
+    d->screen_root_pixmap_changed = sc->screen_root_pixmap_changed;
     d->window_show = sc->window_show;
     d->window_hide = sc->window_hide;
     d->window_resize = sc->window_resize;
     screen_add_plugin_data(sc, plugin_id, d);
 
-    sc->screen_paint = render_screen_paint;
+    sc->screen_paint = render_paint;
+    sc->screen_root_pixmap_changed = render_root_pixmap_changed;
     sc->window_show = render_window_show;
     sc->window_hide = render_window_hide;
     sc->window_resize = render_window_resize;
@@ -67,11 +89,13 @@ render_init(d_screen_t *sc, int id)
     d->pict_formats = xcb_render_query_pict_formats_reply(sc->dpy->conn, ck,
                                                           NULL);
 
-    format = find_visual_format(d, sc->super.root_visual);
+    d->root_format = find_visual_format(d, sc->super.root_visual);
+    d->argb32_format = find_argb32_format(d);
+    d->root_picture = XCB_NONE;
 
     d->overlay_picture = xcb_generate_id(sc->dpy->conn);
     xcb_render_create_picture(sc->dpy->conn,
-                              d->overlay_picture, sc->overlay, format,
+                              d->overlay_picture, sc->overlay, d->root_format,
                               0, NULL);
 
     /* make the double buffer */
@@ -81,10 +105,24 @@ render_init(d_screen_t *sc, int id)
                       sc->super.height_in_pixels);
     d->overlay_buffer = xcb_generate_id(sc->dpy->conn);
     xcb_render_create_picture(sc->dpy->conn, d->overlay_buffer, px,
-                              format, 0, 0);
+                              d->root_format, 0, 0);
     xcb_free_pixmap(sc->dpy->conn, px);
 
-    d->solid_bg = solid_picture(sc, 1.0, 0.0, 0.0, 0.0);
+    d->solid_bg = solid_picture(d, sc, 1.0, 0.0, 0.0, 0.0);
+
+    d->all_region = xcb_generate_id(sc->dpy->conn);
+    d->paint_region = xcb_generate_id(sc->dpy->conn);
+    d->shadow_region = xcb_generate_id(sc->dpy->conn);
+    rect.x = rect.y = 0;
+    rect.width = sc->super.width_in_pixels;
+    rect.height = sc->super.height_in_pixels;
+    xcb_xfixes_create_region(sc->dpy->conn, d->all_region, 1, &rect);
+    xcb_xfixes_create_region(sc->dpy->conn, d->paint_region, 1, &rect);
+    xcb_xfixes_create_region(sc->dpy->conn, d->shadow_region, 1, &rect);
+
+    d->shadowalpha = 0.2;
+    d->xshadowoff = 2;
+    d->yshadowoff = 2;
 }
 
 void
@@ -93,15 +131,25 @@ render_free(d_screen_t *sc)
     data_t *d = screen_find_plugin_data(sc, plugin_id);
     free(d->pict_formats);
     xcb_render_free_picture(sc->dpy->conn, d->solid_bg);
+    if (d->root_picture)
+        xcb_render_free_picture(sc->dpy->conn, d->root_picture);
     xcb_render_free_picture(sc->dpy->conn, d->overlay_picture);
     xcb_render_free_picture(sc->dpy->conn, d->overlay_buffer);
+    xcb_xfixes_destroy_region(sc->dpy->conn, d->all_region);
+    xcb_xfixes_destroy_region(sc->dpy->conn, d->paint_region);
+    xcb_xfixes_destroy_region(sc->dpy->conn, d->shadow_region);
     free(d);
+    screen_remove_plugin_data(sc, plugin_id);
 }
 
 void
 render_window_free(d_window_t *w, window_data_t *wd)
 {
     render_free_picture(w, wd);
+    if (wd->shadow_picture) {
+        xcb_render_free_picture(w->sc->dpy->conn, wd->shadow_picture);
+        wd->shadow_picture = XCB_NONE;
+    }
     free(wd);
 }
 
@@ -110,6 +158,7 @@ render_window_show(d_window_t *w)
 {
     data_t *d;
     window_data_t *wd;
+    xcb_rectangle_t rect;
 
     d = screen_find_plugin_data(w->sc, plugin_id);
 
@@ -122,7 +171,14 @@ render_window_show(d_window_t *w)
    
     wd = malloc(sizeof(window_data_t));
     wd->picture = XCB_NONE;
-    wd->ck_picture.sequence = 0;
+    wd->shadow_picture = solid_picture(d, w->sc, d->shadowalpha,
+                                       0.0, 0.0, 0.0);
+
+    wd->paint_clip = xcb_generate_id(w->sc->dpy->conn);
+    rect.x = rect.y = 0;
+    rect.width = rect.height = 1;
+    xcb_xfixes_create_region(w->sc->dpy->conn, wd->paint_clip, 1, &rect);
+
     window_add_plugin_data(w, plugin_id, wd);
 
     window_ref(w);
@@ -138,35 +194,69 @@ render_window_hide(d_window_t *w)
     wd = window_find_plugin_data(w, plugin_id);
     if (wd) {
         render_window_free(w, wd);
+        xcb_xfixes_destroy_region(w->sc->dpy->conn, wd->paint_clip);
         window_remove_plugin_data(w, plugin_id);
     }
 
-    window_unref(w);
-
     /* pass it on */
     d->window_hide(w);
 }
 
-static
-xcb_render_picture_t solid_picture(d_screen_t *sc,
-                                   double a, double r,
-                                   double g, double b)
+static xcb_render_picture_t
+solid_picture(data_t *d, d_screen_t *sc,
+              double a, double r, double g, double b)
 {
+    xcb_pixmap_t pixmap;
     xcb_render_picture_t picture;
     xcb_render_color_t   c;
+    const uint32_t vals = XCB_RENDER_REPEAT_NORMAL;
+    const xcb_rectangle_t rect = { 0, 0, 1, 1 };
 
-    picture = xcb_generate_id (sc->dpy->conn);
+    pixmap = xcb_generate_id(sc->dpy->conn);
+    picture = xcb_generate_id(sc->dpy->conn);
+
+    xcb_create_pixmap(sc->dpy->conn, 32, pixmap, sc->super.root, 1, 1);
+    xcb_render_create_picture(sc->dpy->conn, picture, pixmap, d->argb32_format,
+                              XCB_RENDER_CP_REPEAT, &vals);
 
     c.alpha = a * 0xffff;
-    c.red   = a * r * 0xffff;
-    c.green = a * g * 0xffff;
-    c.blue  = a * b * 0xffff;
+    c.red = r * 0xffff;
+    c.green = g * 0xffff;
+    c.blue = b * 0xffff;
+
+    xcb_render_fill_rectangles(sc->dpy->conn, XCB_RENDER_PICT_OP_SRC,
+                               picture, c, 1, &rect);
+    xcb_free_pixmap(sc->dpy->conn, pixmap);
 
-    xcb_render_create_solid_fill (sc->dpy->conn, picture, c);
     return picture;
 }
 
 static xcb_render_pictformat_t
+find_argb32_format(data_t *d)
+{
+    xcb_render_pictforminfo_iterator_t it;
+
+    for (it = xcb_render_query_pict_formats_formats_iterator(d->pict_formats);
+         it.rem; xcb_render_pictforminfo_next(&it))
+    {
+        xcb_render_pictforminfo_t *format = it.data;
+        if (format->type == XCB_RENDER_PICT_TYPE_DIRECT) {
+            if (format->depth              == 32   &&
+                format->direct.alpha_mask  == 0xff &&
+                format->direct.red_mask    == 0xff &&
+                format->direct.green_mask  == 0xff &&
+                format->direct.blue_mask   == 0xff &&
+                format->direct.alpha_shift == 24   &&
+                format->direct.red_shift   == 16   &&
+                format->direct.green_shift == 8    &&
+                format->direct.blue_shift  == 0)
+                return format->id;
+        }
+    }
+    return XCB_NONE;
+}
+
+static xcb_render_pictformat_t
 find_visual_format(data_t *d, xcb_visualid_t visual)
 {
     xcb_render_pictscreen_iterator_t si;
@@ -190,37 +280,32 @@ find_visual_format(data_t *d, xcb_visualid_t visual)
     return XCB_NONE;
 }
 
-static xcb_render_picture_t
-render_get_picture(d_window_t *w, window_data_t *wd)
+static void
+render_free_picture(d_window_t *w, window_data_t *wd)
 {
-    if (wd->ck_picture.sequence) {
-        xcb_generic_error_t *err;
-        //printf("** checking create picture 0x%x\n", w->id);
-        err = xcb_request_check(w->sc->dpy->conn, wd->ck_picture);
-        if (err) {
-            wd->picture = XCB_NONE;
-            printf("error creating picture for window 0x%x\n", w->id);
-            free(err);
-        }
-        wd->ck_picture.sequence = 0;
+    /* this might cause an error, oh well */
+    if (wd->picture) {
+        xcb_render_free_picture(w->sc->dpy->conn, wd->picture);
+        wd->picture = XCB_NONE;
     }
-    //printf("returning picture 0x%x for window 0x%x\n", wd->picture, w->id);
-    return wd->picture;
 }
 
 static void
-render_free_picture(d_window_t *w, window_data_t *wd)
+render_update_root_picture(d_screen_t *sc, data_t *d)
 {
-    xcb_render_picture_t pict;
+    xcb_pixmap_t px;
 
-    pict = render_get_picture(w, wd);
-    if (pict) xcb_render_free_picture(w->sc->dpy->conn, pict);
-    wd->picture = XCB_NONE;
+    px = screen_get_root_pixmap(sc);
+    if (px) {
+        d->root_picture = xcb_generate_id(sc->dpy->conn);
+        xcb_render_create_picture_checked(sc->dpy->conn,
+                                          d->root_picture, px,
+                                          d->root_format, 0, NULL);
+    }
 }
 
 static void
-render_update_picture(d_window_t *w, data_t *d, window_data_t *wd,
-                      gboolean children)
+render_update_picture(d_window_t *w, data_t *d, window_data_t *wd)
 {
     xcb_pixmap_t px;
 
@@ -228,19 +313,19 @@ render_update_picture(d_window_t *w, data_t *d, window_data_t *wd,
     //printf("got pixmap 0x%x\n", px);
     if (px) {
         xcb_render_pictformat_t format;
-        const uint32_t vals = (children ?
-                               XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS :
-                               XCB_SUBWINDOW_MODE_CLIP_BY_CHILDREN);
+        const uint32_t vals = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS;
 
         render_free_picture(w, wd);
 
         wd->picture = xcb_generate_id(w->sc->dpy->conn);
         format = find_visual_format(d, window_get_visual(w));
-        wd->ck_picture =
-            xcb_render_create_picture_checked(w->sc->dpy->conn,
-                                              wd->picture, px, format,
-                                              XCB_RENDER_CP_SUBWINDOW_MODE,
-                                              &vals);
+        /* we don't need to check this.  if it fails, we'll just be drawing
+           an invalid picture and creating some X errors but that's no big
+           deal really */
+        xcb_render_create_picture(w->sc->dpy->conn,
+                                  wd->picture, px, format,
+                                  XCB_RENDER_CP_SUBWINDOW_MODE,
+                                  &vals);
     }
 }
 
@@ -252,27 +337,117 @@ render_window_resize(d_window_t *w)
 
     d = screen_find_plugin_data(w->sc, plugin_id);
     wd = window_find_plugin_data(w, plugin_id);
-    render_free_picture(w, wd);
 
     /* pass it on */
     d->window_resize(w);
+
+    assert(wd != NULL);
+    render_free_picture(w, wd);
+}
+
+static void
+render_root_pixmap_changed(d_screen_t *sc)
+{
+    data_t *d;
+
+    d = screen_find_plugin_data(sc, plugin_id);
+    if (d->root_picture) {
+        xcb_render_free_picture(sc->dpy->conn, d->root_picture);
+        d->root_picture = XCB_NONE;
+    }
+
+    /* pass it on */
+    d->screen_root_pixmap_changed(sc);
 }
 
 static void
-render_screen_paint(d_screen_t *sc)
+render_paint(d_screen_t *sc)
 {
     data_t *d = screen_find_plugin_data(sc, plugin_id);
     d_list_it_t *it;
 
+    xcb_xfixes_copy_region(sc->dpy->conn, d->all_region, d->paint_region);
+
     //printf("-- painting --\n");
+    for (it = list_top(sc->stacking); it; it = it->next) {
+        d_window_t *w = it->data;
+
+        if (!window_is_input_only(w) && window_is_mapped(w)) {
+            gboolean opaque = !window_is_argb(w);
+            window_data_t *wd;
+
+            wd = window_find_plugin_data(w, plugin_id);
+
+            if (opaque) {
+
+                paint_window(w, d, wd, opaque);
+
+                /* remove this window from the paint region, as nothing is
+                   above it, so nothing should draw to this space again */
+                xcb_xfixes_subtract_region(sc->dpy->conn, d->paint_region,
+                                           window_get_region(w),
+                                           d->paint_region);
+                xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
+                                                   d->overlay_buffer,
+                                                   d->paint_region,
+                                                   0, 0);
+            }
+
+            /* save the clip region, when drawing windows (and shadows)
+               below this window, they should use this clip region */
+            xcb_xfixes_copy_region(sc->dpy->conn, d->paint_region,
+                                   wd->paint_clip);
+        }
+    }
+
     paint_root(sc, d);
-#if 1
+
+    xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
+                                       d->overlay_buffer,
+                                       d->paint_region,
+                                       0, 0);
+
     for (it = list_bottom(sc->stacking); it; it = it->prev) {
         d_window_t *w = it->data;
-        if (!window_is_input_only(w) && window_is_mapped(w))
-            paint_window(w, d);
+
+        if (!window_is_input_only(w) && window_is_mapped(w)) {
+            window_data_t *wd;
+            gboolean opaque = !window_is_argb(w);
+
+            wd = window_find_plugin_data(w, plugin_id);
+
+            if (opaque) {
+                /* shape the shadow to the window */
+                xcb_xfixes_copy_region(sc->dpy->conn, window_get_region(w),
+                                       d->shadow_region);
+                xcb_xfixes_translate_region(sc->dpy->conn, d->shadow_region,
+                                            d->xshadowoff, d->yshadowoff);
+                xcb_xfixes_intersect_region(sc->dpy->conn,
+                                            wd->paint_clip, d->shadow_region,
+                                            d->shadow_region);
+                xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
+                                                   d->overlay_buffer,
+                                                   d->shadow_region,
+                                                   0, 0);
+                paint_shadow(w, d, wd);
+            }
+            else {
+                /* use the clip region of the highest opaque window seen so
+                   far, as nothing should be able to draw on top of that region
+                */
+                xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
+                                                   d->overlay_buffer,
+                                                   wd->paint_clip,
+                                                   0, 0);
+                paint_window(w, d, wd, opaque);
+            }
+        }
     }
-#endif
+
+    xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
+                                       d->overlay_buffer,
+                                       d->all_region,
+                                       0, 0);
 
     /* copy the double buffer to the overlay window */
     xcb_render_composite(sc->dpy->conn,
@@ -292,9 +467,23 @@ render_screen_paint(d_screen_t *sc)
 static void
 paint_root(d_screen_t *sc, data_t *d)
 {
+    xcb_render_picture_t src;
+    int op;
+
+    if (!d->root_picture)
+        render_update_root_picture(sc, d);
+
+    if (d->root_picture) {
+        src = d->root_picture;
+        op = XCB_RENDER_PICT_OP_SRC;
+    }
+    else {
+        src = d->solid_bg;
+        op = XCB_RENDER_PICT_OP_CLEAR;
+    }
     xcb_render_composite(sc->dpy->conn,
-                         XCB_RENDER_PICT_OP_CLEAR,
-                         d->solid_bg,
+                         op,
+                         src,
                          XCB_NONE,
                          d->overlay_buffer,
                          0, 0, 0, 0,
@@ -304,26 +493,43 @@ paint_root(d_screen_t *sc, data_t *d)
 }
 
 static void
-paint_window(d_window_t *w, data_t *d)
+paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque)
 {
-    window_data_t *wd;
-    xcb_render_picture_t pict;
-    int x, y, width, height, bwidth;
-
-    wd = window_find_plugin_data(w, plugin_id);
-
     if (!wd->picture)
-        render_update_picture(w, d, wd, TRUE);
-    pict = render_get_picture(w, wd);
+        render_update_picture(w, d, wd);
 
     //printf("-- paint window 0x%x picture 0x%x --\n", w->id, wd->picture);
+    if (wd->picture) {
+        int x, y, width, height, bwidth;
+        int op;
+
+        window_get_area(w, &x, &y, &width, &height, &bwidth);
+        op = !opaque ?
+            XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC;
+
+        xcb_render_composite(w->sc->dpy->conn,
+                             op,
+                             wd->picture,
+                             XCB_NONE,
+                             d->overlay_buffer,
+                             0, 0, 0, 0,
+                             x, y, width + bwidth*2, height + bwidth *2);
+    }
+}
+
+static void
+paint_shadow(d_window_t *w, data_t *d, window_data_t *wd)
+{
+    int x, y, width, height, bwidth;
+
     window_get_area(w, &x, &y, &width, &height, &bwidth);
     xcb_render_composite(w->sc->dpy->conn,
-                         //XCB_RENDER_PICT_OP_SRC,  /* - for solid */
-                         XCB_RENDER_PICT_OP_OVER, /* - for argb */
-                         wd->picture,
+                         XCB_RENDER_PICT_OP_OVER,
+                         wd->shadow_picture,
                          XCB_NONE,
                          d->overlay_buffer,
                          0, 0, 0, 0,
-                         x, y, width + bwidth*2, height + bwidth *2);
+                         x+d->xshadowoff, y+d->yshadowoff,
+                         width + bwidth*2, height + bwidth *2);
+
 }