diff options
Diffstat (limited to 'src/cwindow.c')
-rw-r--r-- | src/cwindow.c | 649 |
1 files changed, 453 insertions, 196 deletions
diff --git a/src/cwindow.c b/src/cwindow.c index 9d7470c5..5698ef54 100644 --- a/src/cwindow.c +++ b/src/cwindow.c @@ -4,8 +4,15 @@ #include "compositor.h" #include "matrix.h" #include <X11/extensions/Xcomposite.h> +#include <math.h> +#include <string.h> -#define SHADOW_OFFSET 3 +#define SHADOW_RADIUS 14 +#define SHADOW_OPACITY (.75) +#define SHADOW_OFFSET -17 + +static Picture shadow_picture (Display *dpy, Window root, + double opacity, int width, int height, int *wp, int *hp); typedef struct Geometry Geometry; typedef struct FreezeInfo FreezeInfo; @@ -46,12 +53,15 @@ struct CWindow XserverRegion border_size; + Picture shadow; + FreezeInfo *freeze_info; unsigned int managed : 1; unsigned int damaged : 1; unsigned int viewable : 1; unsigned int input_only : 1; + unsigned int translucent : 1; unsigned int screen_index : 8; @@ -59,6 +69,9 @@ struct CWindow Distortion *distortions; int n_distortions; + + int shadow_width; + int shadow_height; #endif }; @@ -87,6 +100,30 @@ cwindow_free (CWindow *cwindow) } #endif /* HAVE_COMPOSITE_EXTENSIONS */ +static void +create_shadow (CWindow *cwindow) +{ + if (!cwindow->shadow) + { + cwindow->shadow = shadow_picture (cwindow_get_xdisplay (cwindow), + cwindow_get_screen (cwindow)->xroot, + SHADOW_OPACITY, + cwindow_get_width (cwindow), + cwindow_get_height (cwindow), + &cwindow->shadow_width, &cwindow->shadow_height); + } +} + +static void +delete_shadow (CWindow *cwindow) +{ + if (cwindow->shadow) + { + XRenderFreePicture (cwindow_get_xdisplay (cwindow), cwindow->shadow); + cwindow->shadow = None; + } +} + #ifdef HAVE_COMPOSITE_EXTENSIONS XserverRegion cwindow_extents (CWindow *cwindow) @@ -103,9 +140,16 @@ cwindow_extents (CWindow *cwindow) r.y = geometry->y; r.width = geometry->width; r.height = geometry->height; - - r.width += SHADOW_OFFSET; - r.height += SHADOW_OFFSET; + + create_shadow (cwindow); + + if (cwindow->shadow) + { + r.x = geometry->x + SHADOW_OFFSET; + r.y = geometry->y + SHADOW_OFFSET; + r.width = cwindow->shadow_width; + r.height = cwindow->shadow_height; + } return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), &r, 1); } @@ -117,17 +161,53 @@ cwindow_get_opaque_region (CWindow *cwindow) XRectangle rect; Geometry *geometry; - if (cwindow->freeze_info) - geometry = &cwindow->freeze_info->geometry; + if (cwindow->translucent) + { + return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), NULL, 0); + } else - geometry = &cwindow->geometry; + { + + if (cwindow->freeze_info) + geometry = &cwindow->freeze_info->geometry; + else + geometry = &cwindow->geometry; + + rect.x = geometry->x; + rect.y = geometry->y; + rect.width = geometry->width; + rect.height = geometry->height; + + return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), &rect, 1); + } +} + +static void +cwindow_queue_paint (CWindow *cwindow) +{ + XserverRegion region; + MetaScreen *screen; - rect.x = geometry->x; - rect.y = geometry->y; - rect.width = geometry->width; - rect.height = geometry->height; + create_shadow (cwindow); + + region = cwindow_extents (cwindow); + screen = cwindow_get_screen (cwindow); + + meta_compositor_invalidate_region (cwindow->compositor, + screen, + region); + + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); +} + +void +cwindow_set_translucent (CWindow *cwindow, gboolean translucent) +{ +#if 0 + cwindow->translucent = !!translucent; +#endif - return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), &rect, 1); + cwindow_queue_paint (cwindow); } Drawable @@ -377,169 +457,16 @@ convert_matrix (Matrix3 *matrix, XTransform *trans) trans->matrix[2][2] = double_to_fixed (matrix->coeff[2][2]); } -#if 0 -static void -get_transform (XTransform *trans, int x, int y, int w, int h) -{ - Matrix3 tmp; - - matrix3_identity (&tmp); - - transform_matrix_perspective (0, 0, w, h, - 0, 0, w - 1 - 0.1 * w, 0, - 0, 0 + h - 1 - 0.1 * h, 0 + w - 1, 0 + h - 1, - - &tmp); - -#if 0 - matrix3_translate (&tmp, 50, 50); -#endif - - matrix3_invert (&tmp); - - convert_matrix (&tmp, trans); -} -#endif - -#if 0 -void -cwindow_draw (CWindow *cwindow, Picture picture) -{ - /* Actually draw the window */ - XRenderPictFormat *format; - Picture wpicture; - XRenderPictureAttributes pa; - XTransform trans; - int x, y, w, h; - - if (cwindow_get_input_only (cwindow)) - return; - - if (!cwindow_get_viewable (cwindow)) - return; - - cwindow_get_paint_bounds (cwindow, &x, &y, &w, &h); - - format = XRenderFindVisualFormat (cwindow_get_xdisplay (cwindow), - cwindow_get_visual (cwindow)); - pa.subwindow_mode = IncludeInferiors; - wpicture = XRenderCreatePicture (cwindow_get_xdisplay (cwindow), - cwindow_get_drawable (cwindow), - format, - CPSubwindowMode, - &pa); - - get_transform (&trans, x, y, w, h); - - if (cwindow_get_last_painted_extents (cwindow)) - cwindow_destroy_last_painted_extents (cwindow); - - cwindow_set_last_painted_extents (cwindow, cwindow_extents (cwindow)); - -#if 0 - meta_topic (META_DEBUG_COMPOSITOR, " Compositing window 0x%lx %d,%d %dx%d\n", - cwindow_get_xwindow (cwindow), - cwindow->x, cwindow->y, - cwindow->width, cwindow->height); -#endif - -#if 0 - { - XGCValues value; - GC gc; - - value.function = GXcopy; - value.subwindow_mode = IncludeInferiors; - - gc = XCreateGC (dpy, screen->xroot, GCFunction | GCSubwindowMode, &value); - XSetForeground (dpy, gc, rand()); - XFixesSetGCClipRegion (dpy, gc, 0, 0, damaged_region); - XFillRectangle (dpy, screen->xroot, gc, 0, 0, - screen->width, screen->height); - XFreeGC (dpy, gc); - XSync (dpy, False); - g_usleep (70000); - } -#endif - - if (cwindow_is_translucent (cwindow)) - { - XRenderColor shadow_color = { 0x0000, 0, 0x0000, 0x70c0 }; - XFixesSetPictureClipRegion (cwindow_get_xdisplay (cwindow), - picture, 0, 0, - damaged_region); - - - - XRenderFillRectangle (cwindow_get_xdisplay (cwindow), PictOpOver, - picture, - &shadow_color, - cwindow_get_x (cwindow) + SHADOW_OFFSET, - cwindow_get_y (cwindow) + SHADOW_OFFSET, - cwindow_get_width (cwindow), - cwindow_get_height (cwindow)); - /* Draw window transparent while resizing */ - XRenderComposite (cwindow_get_xdisplay (cwindow), PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ - wpicture, - cwindow_get_screen (cwindow)->trans_picture, - picture, - 0, 0, 0, 0, - x, y, w, h); - } - else - { -#if 0 - XRenderSetPictureTransform (cwindow_get_xdisplay (cwindow), - wpicture, - &trans); - XRenderSetPictureFilter (cwindow_get_xdisplay (cwindow), wpicture, "bilinear", 0, 0); -#endif - - /* Draw window normally */ - XRenderColor shadow_color = { 0x0000, 0, 0x0000, 0x70c0 }; - -#if 0 - XFixesSetPictureClipRegion (dpy, - picture, 0, 0, - region_below); -#endif - - XFixesSetPictureClipRegion (cwindow_get_xdisplay (cwindow), - picture, 0, 0, - damaged_region); - - /* superlame drop shadow */ - XRenderFillRectangle (cwindow_get_xdisplay (cwindow), PictOpOver, - picture, - &shadow_color, - cwindow_get_x (cwindow) + SHADOW_OFFSET, - cwindow_get_y (cwindow) + SHADOW_OFFSET, - cwindow_get_width (cwindow), - cwindow_get_height (cwindow)); - - XRenderComposite (cwindow_get_xdisplay (cwindow), - PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ - wpicture, - None, - picture, - 0, 0, 0, 0, - x, y, w, h); - - } - XRenderFreePicture (cwindow_get_xdisplay (cwindow), wpicture); -} -#endif - gboolean cwindow_is_translucent (CWindow *cwindow) { return FALSE; - MetaCompositor *compositor = cwindow_get_compositor (cwindow); - MetaWindow *window = meta_display_lookup_x_window (meta_compositor_get_display (compositor), cwindow_get_xwindow (cwindow)); - return (window != NULL && - window == meta_compositor_get_display (compositor)->grab_window && - (meta_grab_op_is_resizing (meta_compositor_get_display (compositor)->grab_op) || - meta_grab_op_is_moving (meta_compositor_get_display (compositor)->grab_op))); + MetaCompositor *compositor = cwindow_get_compositor (cwindow); + MetaWindow *window = meta_display_lookup_x_window (meta_compositor_get_display (compositor), cwindow_get_xwindow (cwindow)); + return (window != NULL && + window == meta_compositor_get_display (compositor)->grab_window && + (meta_grab_op_is_resizing (meta_compositor_get_display (compositor)->grab_op) || + meta_grab_op_is_moving (meta_compositor_get_display (compositor)->grab_op))); } @@ -761,8 +688,8 @@ cwindow_process_damage_notify (CWindow *cwindow, XDamageNotifyEvent *event) void cwindow_process_configure_notify (CWindow *cwindow, XConfigureEvent *event) { - XserverRegion region; MetaScreen *screen; + int old_width, old_height; screen = cwindow_get_screen (cwindow); @@ -774,20 +701,26 @@ cwindow_process_configure_notify (CWindow *cwindow, XConfigureEvent *event) cwindow_set_x (cwindow, event->x); cwindow_set_y (cwindow, event->y); + old_width = cwindow_get_width (cwindow); cwindow_set_width (cwindow, event->width); + old_height = cwindow_get_height (cwindow); cwindow_set_height (cwindow, event->height); cwindow_set_border_width (cwindow, event->border_width); if (cwindow->freeze_info) - return; - - region = cwindow_extents (cwindow); - - meta_compositor_invalidate_region (cwindow->compositor, - screen, - region); - - XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); + { + return; + } + else + { + if (old_width != cwindow_get_width (cwindow) || + old_height != cwindow_get_height (cwindow)) + { + delete_shadow (cwindow); + } + + cwindow_queue_paint (cwindow); + } } void @@ -852,14 +785,9 @@ cwindow_thaw (CWindow *cwindow) g_free (cwindow->freeze_info); cwindow->freeze_info = NULL; - region = cwindow_extents (cwindow); - - meta_compositor_invalidate_region (cwindow->compositor, - cwindow_get_screen (cwindow), - region); + delete_shadow (cwindow); - XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); - + cwindow_queue_paint (cwindow); } void @@ -930,20 +858,26 @@ cwindow_draw (CWindow *cwindow, Picture destination) XFixesSetPictureClipRegion (dpy, destination, 0, 0, shadow_clip); - /* superlame drop shadow */ - XRenderFillRectangle (cwindow_get_xdisplay (cwindow), - PictOpOver, - destination, - &shadow_color, + /* super drop shadow */ + if (!cwindow->translucent) + { + int hp, wp; + create_shadow (cwindow); + + XRenderComposite (cwindow_get_xdisplay (cwindow), PictOpOver, + cwindow->shadow, None, destination, + 0, 0, + 0, 0, geometry->x + SHADOW_OFFSET, geometry->y + SHADOW_OFFSET, - geometry->width, geometry->height); - + cwindow->shadow_width, cwindow->shadow_height); + } + XFixesSetPictureClipRegion (dpy, destination, 0, 0, old_clip); XRenderComposite (cwindow_get_xdisplay (cwindow), PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ picture, - None, + cwindow->translucent? cwindow_get_screen (cwindow)->trans_picture : None, destination, 0, 0, 0, 0, geometry->x, @@ -962,3 +896,326 @@ cwindow_draw (CWindow *cwindow, Picture destination) XRenderFreePicture (cwindow_get_xdisplay (cwindow), picture); } + + +/* + * Gaussian shadows + */ + +/* + * FIXME: move these into MetaDisplay + */ + +/* For shadow precomputation */ +typedef struct _conv { + int size; + double *data; +} conv; + +int Gsize = -1; +unsigned char *shadowCorner = NULL; +unsigned char *shadowTop = NULL; +conv *gaussianMap; + +static double +gaussian (double r, double x, double y) +{ + return ((1 / (sqrt (2 * M_PI * r))) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + +static conv * +make_gaussian_map (Display *dpy, double r) +{ + conv *c; + int size = ((int) ceil ((r * 3)) + 1) & ~1; + int center = size / 2; + int x, y; + double t; + double g; + + c = g_malloc (sizeof (conv) + size * size * sizeof (double)); + c->size = size; + c->data = (double *) (c + 1); + t = 0.0; + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + g = gaussian (r, (double) (x - center), (double) (y - center)); + t += g; + c->data[y * size + x] = g; + } +/* printf ("gaussian total %f\n", t); */ + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + c->data[y*size + x] /= t; + } + return c; +} + +/* + * A picture will help + * + * -center 0 width width+center + * -center +-----+-------------------+-----+ + * | | | | + * | | | | + * 0 +-----+-------------------+-----+ + * | | | | + * | | | | + * | | | | + * height +-----+-------------------+-----+ + * | | | | + * height+ | | | | + * center +-----+-------------------+-----+ + */ + +static unsigned char +sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) +{ + int fx, fy; + double *g_data; + double *g_line = map->data; + int g_size = map->size; + int center = g_size / 2; + int fx_start, fx_end; + int fy_start, fy_end; + double v; + + /* + * Compute set of filter values which are "in range", + * that's the set with: + * 0 <= x + (fx-center) && x + (fx-center) < width && + * 0 <= y + (fy-center) && y + (fy-center) < height + * + * 0 <= x + (fx - center) x + fx - center < width + * center - x <= fx fx < width + center - x + */ + + fx_start = center - x; + if (fx_start < 0) + fx_start = 0; + fx_end = width + center - x; + if (fx_end > g_size) + fx_end = g_size; + + fy_start = center - y; + if (fy_start < 0) + fy_start = 0; + fy_end = height + center - y; + if (fy_end > g_size) + fy_end = g_size; + + g_line = g_line + fy_start * g_size + fx_start; + + v = 0; + for (fy = fy_start; fy < fy_end; fy++) + { + g_data = g_line; + g_line += g_size; + + for (fx = fx_start; fx < fx_end; fx++) + v += *g_data++; + } + if (v > 1) + v = 1; + + return ((unsigned char) (v * opacity * 255.0)); +} + +/* precompute shadow corners and sides to save time for large windows */ +static void +presum_gaussian (conv *map) +{ + int center = map->size/2; + int opacity, x, y; + + Gsize = map->size; + + if (shadowCorner) + g_free ((void *)shadowCorner); + if (shadowTop) + g_free ((void *)shadowTop); + + shadowCorner = (unsigned char *)(g_malloc ((Gsize + 1) * (Gsize + 1) * 26)); + shadowTop = (unsigned char *)(g_malloc ((Gsize + 1) * 26)); + + for (x = 0; x <= Gsize; x++) + { + shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2); + for(opacity = 0; opacity < 25; opacity++) + shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25; + for(y = 0; y <= x; y++) + { + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2); + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + for(opacity = 0; opacity < 25; opacity++) + shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25; + } + } +} + +static XImage * +make_shadow (Display *dpy, double opacity, int width, int height) +{ + XImage *ximage; + unsigned char *data; + int gsize = gaussianMap->size; + int ylimit, xlimit; + int swidth = width + gsize; + int sheight = height + gsize; + int center = gsize / 2; + int x, y; + unsigned char d; + int x_diff; + int opacity_int = (int)(opacity * 25); + data = g_malloc (swidth * sheight * sizeof (unsigned char)); + if (!data) + return 0; + ximage = XCreateImage (dpy, + DefaultVisual(dpy, DefaultScreen(dpy)), + 8, + ZPixmap, + 0, + (char *) data, + swidth, sheight, 8, swidth * sizeof (unsigned char)); + if (!ximage) + { + g_free (data); + return 0; + } + /* + * Build the gaussian in sections + */ + + /* + * center (fill the complete data array) + */ + if (Gsize > 0) + d = shadowTop[opacity_int * (Gsize + 1) + Gsize]; + else + d = sum_gaussian (gaussianMap, opacity, center, center, width, height); + memset(data, d, sheight * swidth); + + /* + * corners + */ + ylimit = gsize; + if (ylimit > sheight / 2) + ylimit = (sheight + 1) / 2; + xlimit = gsize; + if (xlimit > swidth / 2) + xlimit = (swidth + 1) / 2; + + for (y = 0; y < ylimit; y++) + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize && ylimit == Gsize) + d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height); + data[y * swidth + x] = d; + data[(sheight - y - 1) * swidth + x] = d; + data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + + /* + * top/bottom + */ + x_diff = swidth - (gsize * 2); + if (x_diff > 0 && ylimit > 0) + { + for (y = 0; y < ylimit; y++) + { + if (ylimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + y]; + else + d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height); + memset (&data[y * swidth + gsize], d, x_diff); + memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff); + } + } + + /* + * sides + */ + + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height); + for (y = gsize; y < sheight - gsize; y++) + { + data[y * swidth + x] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + } + + return ximage; +} + +static Picture +shadow_picture (Display *dpy, Window root, + double opacity, int width, int height, int *wp, int *hp) +{ + XImage *shadowImage; + Pixmap shadowPixmap; + Picture shadowPicture; + GC gc; + + if (!gaussianMap) + { + gaussianMap = make_gaussian_map(dpy, SHADOW_RADIUS); + presum_gaussian (gaussianMap); + } + + shadowImage = make_shadow (dpy, opacity, width, height); + if (!shadowImage) + return None; + shadowPixmap = XCreatePixmap (dpy, root, + shadowImage->width, + shadowImage->height, + 8); + if (!shadowPixmap) + { + XDestroyImage (shadowImage); + return None; + } + + shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, + XRenderFindStandardFormat (dpy, PictStandardA8), + 0, 0); + if (!shadowPicture) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return None; + } + + gc = XCreateGC (dpy, shadowPixmap, 0, 0); + if (!gc) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + XRenderFreePicture (dpy, shadowPicture); + return None; + } + + XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, + shadowImage->width, + shadowImage->height); + *wp = shadowImage->width; + *hp = shadowImage->height; + XFreeGC (dpy, gc); + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return shadowPicture; +} |