diff --git a/src/compositor/meta-shadow-factory-private.h b/src/compositor/meta-shadow-factory-private.h index 3d51cbbc5..e6b033e8a 100644 --- a/src/compositor/meta-shadow-factory-private.h +++ b/src/compositor/meta-shadow-factory-private.h @@ -47,7 +47,8 @@ void meta_shadow_paint (MetaShadow *shadow, int window_width, int window_height, guint8 opacity, - cairo_region_t *clip); + cairo_region_t *clip, + gboolean clip_strictly); void meta_shadow_get_bounds (MetaShadow *shadow, int window_x, int window_y, diff --git a/src/compositor/meta-shadow-factory.c b/src/compositor/meta-shadow-factory.c index 1a9a447f7..f8bb1a838 100644 --- a/src/compositor/meta-shadow-factory.c +++ b/src/compositor/meta-shadow-factory.c @@ -189,8 +189,10 @@ meta_shadow_unref (MetaShadow *shadow) * @window_width: actual width of the region to paint a shadow for * @window_height: actual height of the region to paint a shadow for * @clip: (allow-none): if non-%NULL specifies the visible portion - * of the shadow. Drawing won't be strictly clipped to this region - * but it will be used to optimize what is drawn. + * of the shadow. + * @clip_strictly: if %TRUE, drawing will be clipped strictly + * to @clip, otherwise, it will be only used to optimize + * drawing. * * Paints the shadow at the given position, for the specified actual * size of the region. (Since a #MetaShadow can be shared between @@ -204,7 +206,8 @@ meta_shadow_paint (MetaShadow *shadow, int window_width, int window_height, guint8 opacity, - cairo_region_t *clip) + cairo_region_t *clip, + gboolean clip_strictly) { float texture_width = cogl_texture_get_width (shadow->texture); float texture_height = cogl_texture_get_height (shadow->texture); @@ -276,6 +279,9 @@ meta_shadow_paint (MetaShadow *shadow, dest_rect.y = dest_y[j]; dest_rect.height = dest_y[j + 1] - dest_y[j]; + if (dest_rect.height == 0) + continue; + for (i = 0; i < n_x; i++) { cairo_region_overlap_t overlap; @@ -283,16 +289,64 @@ meta_shadow_paint (MetaShadow *shadow, dest_rect.x = dest_x[i]; dest_rect.width = dest_x[i + 1] - dest_x[i]; + if (dest_rect.width == 0) + continue; + if (clip) overlap = cairo_region_contains_rectangle (clip, &dest_rect); else - overlap = CAIRO_REGION_OVERLAP_PART; + overlap = CAIRO_REGION_OVERLAP_IN; - if (overlap != CAIRO_REGION_OVERLAP_OUT) - cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j], - dest_x[i + 1], dest_y[j + 1], - src_x[i], src_y[j], - src_x[i + 1], src_y[j + 1]); + /* There's quite a bit of overhead from allocating a new + * region in order to find an exact intersection and + * generating more geometry - we make the assumption that + * unless we have to clip strictly it will be cheaper to + * just draw the entire rectangle. + */ + if (overlap == CAIRO_REGION_OVERLAP_IN || + (overlap == CAIRO_REGION_OVERLAP_PART && !clip_strictly)) + { + cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j], + dest_x[i + 1], dest_y[j + 1], + src_x[i], src_y[j], + src_x[i + 1], src_y[j + 1]); + } + else if (overlap == CAIRO_REGION_OVERLAP_PART) + { + cairo_region_t *intersection; + int n_rectangles, k; + + intersection = cairo_region_create_rectangle (&dest_rect); + cairo_region_intersect (intersection, clip); + + n_rectangles = cairo_region_num_rectangles (intersection); + for (k = 0; k < n_rectangles; k++) + { + cairo_rectangle_int_t rect; + float src_x1, src_x2, src_y1, src_y2; + + cairo_region_get_rectangle (intersection, k, &rect); + + /* Separately linear interpolate X and Y coordinates in the source + * based on the destination X and Y coordinates */ + + src_x1 = (src_x[i] * (dest_rect.x + dest_rect.width - rect.x) + + src_x[i + 1] * (rect.x - dest_rect.x)) / dest_rect.width; + src_x2 = (src_x[i] * (dest_rect.x + dest_rect.width - (rect.x + rect.width)) + + src_x[i + 1] * (rect.x + rect.width - dest_rect.x)) / dest_rect.width; + + src_y1 = (src_y[j] * (dest_rect.y + dest_rect.height - rect.y) + + src_y[j + 1] * (rect.y - dest_rect.y)) / dest_rect.height; + src_y2 = (src_y[j] * (dest_rect.y + dest_rect.height - (rect.y + rect.height)) + + src_y[j + 1] * (rect.y + rect.height - dest_rect.y)) / dest_rect.height; + + cogl_rectangle_with_texture_coords (rect.x, rect.y, + rect.x + rect.width, rect.y + rect.height, + src_x1, src_y1, src_x2, src_y2); + } + + cairo_region_destroy (intersection); + } } } } diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 3be036320..4aab5c963 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -702,6 +702,26 @@ meta_window_actor_get_shadow_bounds (MetaWindowActor *self, } #endif +/* If we have an ARGB32 window that we decorate with a frame, it's + * probably something like a translucent terminal - something where + * the alpha channel represents transparency rather than a shape. We + * don't want to show the shadow through the translucent areas since + * the shadow is wrong for translucent windows (it should be + * translucent itself and colored), and not only that, will /look/ + * horribly wrong - a misplaced big black blob. As a hack, what we + * want to do is just draw the shadow as normal outside the frame, and + * inside the frame draw no shadow. This is also not even close to + * the right result, but looks OK. We also apply this approach to + * windows set to be partially translucent with _NET_WM_WINDOW_OPACITY. + */ +static gboolean +clip_shadow_under_window (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + + return (priv->argb32 || priv->opacity != 0xff) && priv->window->frame; +} + static void meta_window_actor_paint (ClutterActor *actor) { @@ -714,17 +734,36 @@ meta_window_actor_paint (ClutterActor *actor) { MetaShadowParams params; cairo_rectangle_int_t shape_bounds; + cairo_region_t *clip = priv->shadow_clip; meta_window_actor_get_shape_bounds (self, &shape_bounds); meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); + /* The frame bounds are already subtracted from priv->shadow_clip + * if that exists. + */ + if (!clip && clip_shadow_under_window (self)) + { + cairo_region_t *frame_bounds = meta_window_get_frame_bounds (priv->window); + cairo_rectangle_int_t bounds; + + meta_window_actor_get_shadow_bounds (self, appears_focused, &bounds); + clip = cairo_region_create_rectangle (&bounds); + + cairo_region_subtract (clip, frame_bounds); + } + meta_shadow_paint (shadow, params.x_offset + shape_bounds.x, params.y_offset + shape_bounds.y, shape_bounds.width, shape_bounds.height, (clutter_actor_get_paint_opacity (actor) * params.opacity * priv->opacity) / (255 * 255), - priv->shadow_clip); + clip, + clip_shadow_under_window (self)); /* clip_strictly - not just as an optimization */ + + if (clip && clip != priv->shadow_clip) + cairo_region_destroy (clip); } CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor); @@ -1787,6 +1826,12 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, { meta_window_actor_clear_shadow_clip (self); priv->shadow_clip = cairo_region_copy (beneath_region); + + if (clip_shadow_under_window (self)) + { + cairo_region_t *frame_bounds = meta_window_get_frame_bounds (priv->window); + cairo_region_subtract (priv->shadow_clip, frame_bounds); + } } } diff --git a/src/core/frame.c b/src/core/frame.c index 4db00028c..155a0b8ad 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -328,7 +328,7 @@ meta_frame_calc_geometry (MetaFrame *frame, *geomp = geom; } -static void +static gboolean update_shape (MetaFrame *frame) { if (frame->need_reapply_frame_shape) @@ -339,10 +339,14 @@ update_shape (MetaFrame *frame) frame->rect.height, frame->window->has_shape); frame->need_reapply_frame_shape = FALSE; + + return TRUE; } + else + return FALSE; } -void +gboolean meta_frame_sync_to_window (MetaFrame *frame, int resize_gravity, gboolean need_move, @@ -350,8 +354,7 @@ meta_frame_sync_to_window (MetaFrame *frame, { if (!(need_move || need_resize)) { - update_shape (frame); - return; + return update_shape (frame); } meta_topic (META_DEBUG_GEOMETRY, @@ -401,6 +404,17 @@ meta_frame_sync_to_window (MetaFrame *frame, meta_ui_repaint_frame (frame->window->screen->ui, frame->xwindow); } + + return need_resize; +} + +cairo_region_t * +meta_frame_get_frame_bounds (MetaFrame *frame) +{ + return meta_ui_get_frame_bounds (frame->window->screen->ui, + frame->xwindow, + frame->rect.width, + frame->rect.height); } void diff --git a/src/core/frame.h b/src/core/frame.h index 019d6b354..7a637e82f 100644 --- a/src/core/frame.h +++ b/src/core/frame.h @@ -73,11 +73,13 @@ Window meta_frame_get_xwindow (MetaFrame *frame); /* These should ONLY be called from meta_window_move_resize_internal */ void meta_frame_calc_geometry (MetaFrame *frame, MetaFrameGeometry *geomp); -void meta_frame_sync_to_window (MetaFrame *frame, +gboolean meta_frame_sync_to_window (MetaFrame *frame, int gravity, gboolean need_move, gboolean need_resize); +cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame); + void meta_frame_set_screen_cursor (MetaFrame *frame, MetaCursor cursor); diff --git a/src/core/window-private.h b/src/core/window-private.h index cbac91d0d..790994384 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -42,6 +42,7 @@ #include "stack.h" #include "iconcache.h" #include +#include #include typedef struct _MetaWindowQueue MetaWindowQueue; @@ -316,6 +317,9 @@ struct _MetaWindow /* if TRUE, application is buggy and SYNC resizing is turned off */ guint disable_sync : 1; + /* if non-NULL, the bounds of the window frame */ + cairo_region_t *frame_bounds; + /* Note: can be NULL */ GSList *struts; diff --git a/src/core/window.c b/src/core/window.c index d44293265..aa6d75cbe 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -181,6 +181,9 @@ meta_window_finalize (GObject *object) if (window->mini_icon) g_object_unref (G_OBJECT (window->mini_icon)); + if (window->frame_bounds) + cairo_region_destroy (window->frame_bounds); + meta_icon_cache_free (&window->icon_cache); g_free (window->sm_client_id); @@ -4295,6 +4298,7 @@ meta_window_move_resize_internal (MetaWindow *window, int frame_size_dy; int size_dx; int size_dy; + gboolean frame_shape_changed = FALSE; gboolean is_configure_request; gboolean do_gravity_adjust; gboolean is_user_action; @@ -4598,9 +4602,9 @@ meta_window_move_resize_internal (MetaWindow *window, meta_window_set_gravity (window, StaticGravity); if (configure_frame_first && window->frame) - meta_frame_sync_to_window (window->frame, - gravity, - need_move_frame, need_resize_frame); + frame_shape_changed = meta_frame_sync_to_window (window->frame, + gravity, + need_move_frame, need_resize_frame); values.border_width = 0; values.x = client_move_x; @@ -4655,9 +4659,9 @@ meta_window_move_resize_internal (MetaWindow *window, } if (!configure_frame_first && window->frame) - meta_frame_sync_to_window (window->frame, - gravity, - need_move_frame, need_resize_frame); + frame_shape_changed = meta_frame_sync_to_window (window->frame, + gravity, + need_move_frame, need_resize_frame); /* Put gravity back to be nice to lesser window managers */ if (use_static_gravity) @@ -4700,6 +4704,12 @@ meta_window_move_resize_internal (MetaWindow *window, * b) all constraints are obeyed by window->rect and frame->rect */ + if (frame_shape_changed && window->frame_bounds) + { + cairo_region_destroy (window->frame_bounds); + window->frame_bounds = NULL; + } + if (meta_prefs_get_attach_modal_dialogs ()) meta_window_foreach_transient (window, move_attached_dialog, NULL); } @@ -10204,3 +10214,24 @@ meta_window_get_frame_type (MetaWindow *window) return base_type; } } + +/** + * meta_window_get_frame_bounds: + * + * Gets a region representing the outer bounds of the window's frame. + * + * Return value: (transfer none) (allow-none): a #cairo_region_t + * holding the outer bounds of the window, or %NULL if the window + * doesn't have a frame. + */ +cairo_region_t * +meta_window_get_frame_bounds (MetaWindow *window) +{ + if (!window->frame_bounds) + { + if (window->frame) + window->frame_bounds = meta_frame_get_frame_bounds (window->frame); + } + + return window->frame_bounds; +} diff --git a/src/meta/window.h b/src/meta/window.h index a1c4b75b8..985a14604 100644 --- a/src/meta/window.h +++ b/src/meta/window.h @@ -23,6 +23,7 @@ #define META_WINDOW_H #include +#include #include #include @@ -156,4 +157,6 @@ const char *meta_window_get_mutter_hints (MetaWindow *window); MetaFrameType meta_window_get_frame_type (MetaWindow *window); +cairo_region_t *meta_window_get_frame_bounds (MetaWindow *window); + #endif diff --git a/src/ui/frames.c b/src/ui/frames.c index 7c62898df..520f29d66 100644 --- a/src/ui/frames.c +++ b/src/ui/frames.c @@ -797,6 +797,122 @@ apply_cairo_region_to_window (Display *display, } #endif +static cairo_region_t * +get_bounds_region (MetaFrames *frames, + MetaUIFrame *frame, + MetaFrameGeometry *fgeom, + int window_width, + int window_height) +{ + cairo_region_t *corners_region; + cairo_region_t *bounds_region; + cairo_rectangle_int_t rect; + + corners_region = cairo_region_create (); + + if (fgeom->top_left_corner_rounded_radius != 0) + { + const int corner = fgeom->top_left_corner_rounded_radius; + const float radius = sqrt(corner) + corner; + int i; + + for (i=0; itop_right_corner_rounded_radius != 0) + { + const int corner = fgeom->top_right_corner_rounded_radius; + const float radius = sqrt(corner) + corner; + int i; + + for (i=0; ibottom_left_corner_rounded_radius != 0) + { + const int corner = fgeom->bottom_left_corner_rounded_radius; + const float radius = sqrt(corner) + corner; + int i; + + for (i=0; ibottom_right_corner_rounded_radius != 0) + { + const int corner = fgeom->bottom_right_corner_rounded_radius; + const float radius = sqrt(corner) + corner; + int i; + + for (i=0; ileft_width; + rect.y = fgeom->top_height; + rect.width = window_width - fgeom->right_width - rect.x; + rect.height = window_height - fgeom->bottom_height - rect.y; + + return cairo_region_create_rectangle (&rect); +} + void meta_frames_apply_shapes (MetaFrames *frames, Window xwindow, @@ -808,8 +924,6 @@ meta_frames_apply_shapes (MetaFrames *frames, /* Apply shapes as if window had new_window_width, new_window_height */ MetaUIFrame *frame; MetaFrameGeometry fgeom; - cairo_rectangle_int_t rect; - cairo_region_t *corners_region; cairo_region_t *window_region; Display *display; @@ -831,7 +945,7 @@ meta_frames_apply_shapes (MetaFrames *frames, meta_topic (META_DEBUG_SHAPES, "Unsetting shape mask on frame 0x%lx\n", frame->xwindow); - + XShapeCombineMask (display, frame->xwindow, ShapeBounding, 0, 0, None, ShapeSet); frame->shape_applied = FALSE; @@ -842,97 +956,14 @@ meta_frames_apply_shapes (MetaFrames *frames, "Frame 0x%lx still doesn't need a shape mask\n", frame->xwindow); } - + return; /* nothing to do */ } - - corners_region = cairo_region_create (); - - if (fgeom.top_left_corner_rounded_radius != 0) - { - const int corner = fgeom.top_left_corner_rounded_radius; - const float radius = sqrt(corner) + corner; - int i; - for (i=0; iframes, xwindow, + window_width, window_height); +} + void meta_ui_queue_frame_draw (MetaUI *ui, Window xwindow) diff --git a/src/ui/ui.h b/src/ui/ui.h index cc449bd51..9a33d9dce 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -104,6 +105,11 @@ void meta_ui_apply_frame_shape (MetaUI *ui, int new_window_height, gboolean window_has_shape); +cairo_region_t *meta_ui_get_frame_bounds (MetaUI *ui, + Window xwindow, + int window_width, + int window_height); + void meta_ui_queue_frame_draw (MetaUI *ui, Window xwindow);