/* * Copyright © 2006 Novell, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: David Reveman */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include int decor_version (void) { return DECOR_INTERFACE_VERSION; } /* decoration property ------------------- data[0] = version data[1] = decoration type data[2] = number of decorations specified in property WINDOW_DECORATION_TYPE_WINDOW property -------------------------------------- data[3] = input left data[4] = input right data[5] = input top data[6] = input bottom data[7] = input left when maximized data[8] = input right when maximized data[9] = input top when maximized data[10] = input bottom when maximized data[11] = min width data[12] = min height fields 13 to 15 are only used by the default decorations on the root window data[13] = frame state data[14] = frame type data[15] = frame actions WINDOW_DECORATION_TYPE_PIXMAP property -------------------------------------- data[3] = pixmap extents frame input is used for creating the input area of the frame window which the client will be reparented into, border is used for positioning data[4] = frame left data[5] = frame right data[6] = frame top data[7] = frame bottom data[8] = input left data[9] = input right data[10] = input top data[11] = input bottom data[12] = frame left when maximized data[13] = frame right when maximized data[14] = frame top when maximized data[15] = frame bottom when maximized data[16] = border left when maximized data[17] = border right when maximized data[18] = border top when maximized data[19] = border bottom when maximized data[20] = min width data[21] = min height fields 22 to 24 are only used by the default decorations on the root window data[22] = frame state data[23] = frame type data[24] = frame actions data[25] = num quads flags 1st to 4nd bit p1 gravity, 5rd to 8th bit p2 gravity, 9rd and 10th bit alignment, 11rd and 12th bit clamp, 13th bit XX, 14th bit XY, 15th bit YX, 16th bit YY. data[26 + n * 9 + 1] = flags data[26 + n * 9 + 2] = p1 x data[26 + n * 9 + 3] = p1 y data[26 + n * 9 + 4] = p2 x data[26 + n * 9 + 5] = p2 y data[26 + n * 9 + 6] = widthMax data[26 + n * 9 + 7] = heightMax data[26 + n * 9 + 8] = x0 data[26 + n * 9 + 9] = y0 */ long * decor_alloc_property (unsigned int n, unsigned int type) { unsigned int propSize = 0; long *data; if (type == WINDOW_DECORATION_TYPE_WINDOW) propSize = WINDOW_PROP_SIZE; else if (type == WINDOW_DECORATION_TYPE_PIXMAP) propSize = BASE_PROP_SIZE + N_QUADS_MAX * QUAD_PROP_SIZE; propSize *= n; propSize += PROP_HEADER_SIZE; data = calloc (propSize, sizeof (long)); data[0] = DECOR_INTERFACE_VERSION; data[1] = type; data[2] = n; return data; } void decor_quads_to_property (long *data, unsigned int n, Pixmap pixmap, decor_extents_t *frame, decor_extents_t *border, decor_extents_t *max_frame, decor_extents_t *max_border, int min_width, int min_height, decor_quad_t *quad, int nQuad, unsigned int frame_type, unsigned int frame_state, unsigned int frame_actions) { /* FIXME: Allocating for N_QUAD_MAX is slightly inefficient, but there * isn't really a better way to do this at the moment */ data += PROP_HEADER_SIZE + n * (BASE_PROP_SIZE + QUAD_PROP_SIZE * N_QUADS_MAX); memcpy (data++, &pixmap, sizeof (Pixmap)); *data++ = frame->left; *data++ = frame->right; *data++ = frame->top; *data++ = frame->bottom; *data++ = border->left; *data++ = border->right; *data++ = border->top; *data++ = border->bottom; *data++ = max_frame->left; *data++ = max_frame->right; *data++ = max_frame->top; *data++ = max_frame->bottom; *data++ = max_border->left; *data++ = max_border->right; *data++ = max_border->top; *data++ = max_border->bottom; *data++ = min_width; *data++ = min_height; *data++ = frame_type; *data++ = frame_state; *data++ = frame_actions; *data++ = nQuad; while (nQuad--) { *data++ = (quad->p1.gravity << 0) | (quad->p2.gravity << 4) | (quad->align << 8) | (quad->clamp << 10) | (quad->stretch << 12) | (quad->m.xx ? XX_MASK : 0) | (quad->m.xy ? XY_MASK : 0) | (quad->m.yx ? YX_MASK : 0) | (quad->m.yy ? YY_MASK : 0); *data++ = quad->p1.x; *data++ = quad->p1.y; *data++ = quad->p2.x; *data++ = quad->p2.y; *data++ = quad->max_width; *data++ = quad->max_height; *data++ = quad->m.x0; *data++ = quad->m.y0; ++quad; } } void decor_gen_window_property (long *data, unsigned int n, decor_extents_t *input, decor_extents_t *max_input, int min_width, int min_height, unsigned int frame_type, unsigned int frame_state, unsigned int frame_actions) { data += PROP_HEADER_SIZE + n * WINDOW_PROP_SIZE; *data++ = input->left; *data++ = input->right; *data++ = input->top; *data++ = input->bottom; *data++ = max_input->left; *data++ = max_input->right; *data++ = max_input->top; *data++ = max_input->bottom; *data++ = min_width; *data++ = min_height; *data++ = frame_type; *data++ = frame_state; *data++ = frame_actions; } int decor_property_get_version (long *data) { return (int) *data; } int decor_property_get_type (long *data) { return (int) data[1]; } int decor_property_get_num (long *data) { return (int) data[2]; } int decor_pixmap_property_to_quads (long *data, unsigned int nOffset, int size, Pixmap *pixmap, decor_extents_t *frame, decor_extents_t *border, decor_extents_t *max_frame, decor_extents_t *max_border, int *min_width, int *min_height, unsigned int *frame_type, unsigned int *frame_state, unsigned int *frame_actions, decor_quad_t *quad) { int i, n, flags; if (size < PROP_HEADER_SIZE + nOffset * (BASE_PROP_SIZE + QUAD_PROP_SIZE + N_QUADS_MAX)) return 0; if (decor_property_get_version (data) != decor_version ()) return 0; if (decor_property_get_type (data) != WINDOW_DECORATION_TYPE_PIXMAP) return 0; data += PROP_HEADER_SIZE + nOffset * (BASE_PROP_SIZE + N_QUADS_MAX * QUAD_PROP_SIZE); memcpy (pixmap, data++, sizeof (Pixmap)); frame->left = *data++; frame->right = *data++; frame->top = *data++; frame->bottom = *data++; border->left = *data++; border->right = *data++; border->top = *data++; border->bottom = *data++; max_frame->left = *data++; max_frame->right = *data++; max_frame->top = *data++; max_frame->bottom = *data++; max_border->left = *data++; max_border->right = *data++; max_border->top = *data++; max_border->bottom = *data++; *min_width = *data++; *min_height = *data++; *frame_type = *data++; *frame_state = *data++; *frame_actions = *data++; n = *data++; for (i = 0; i < n; ++i) { flags = *data++; quad->p1.gravity = (flags >> 0) & 0xf; quad->p2.gravity = (flags >> 4) & 0xf; quad->align = (flags >> 8) & 0x3; quad->clamp = (flags >> 10) & 0x3; quad->stretch = (flags >> 12) & 0x3; quad->m.xx = (flags & XX_MASK) ? 1.0f : 0.0f; quad->m.xy = (flags & XY_MASK) ? 1.0f : 0.0f; quad->m.yx = (flags & YX_MASK) ? 1.0f : 0.0f; quad->m.yy = (flags & YY_MASK) ? 1.0f : 0.0f; quad->p1.x = *data++; quad->p1.y = *data++; quad->p2.x = *data++; quad->p2.y = *data++; quad->max_width = *data++; quad->max_height = *data++; quad->m.x0 = *data++; quad->m.y0 = *data++; ++quad; } return n; } static int decor_point_cmp (const decor_point_t *a, const decor_point_t *b) { /* Use binary | to avoid branch prediction slow-downs */ return (a->x - b->x) | (a->y - b->y) | (a->gravity - b->gravity); } int decor_shadow_options_cmp (const decor_shadow_options_t *a, const decor_shadow_options_t *b) { return (a->shadow_radius != b->shadow_radius) || (a->shadow_opacity != b->shadow_opacity) || (a->shadow_offset_x != b->shadow_offset_x) || (a->shadow_offset_y != b->shadow_offset_y) || memcmp (a->shadow_color, b->shadow_color, sizeof (unsigned short) * 3); } static int decor_matrix_cmp (const decor_matrix_t *a, const decor_matrix_t *b) { return (a->xx != b->xx) || (a->yx != b->yx) || (a->xy != b->xy) || (a->yy != b->yy) || (a->x0 != b->x0) || (a->y0 != b->y0); } static int decor_quad_cmp (const decor_quad_t *a, const decor_quad_t *b) { return decor_point_cmp (&a->p1, &b->p1) || decor_point_cmp (&a->p2, &b->p2) || decor_matrix_cmp (&a->m, &b->m) || ( (a->max_width - b->max_width) | (a->max_height - b->max_height) | (a->align - b->align) | (a->clamp - b->clamp) | (a->stretch - b->stretch) ); } int decor_extents_cmp (const decor_extents_t *a, const decor_extents_t *b) { /* Use binary | to avoid branch prediction slow-downs */ return (a->left - b->left) | (a->right - b->right) | (a->top - b->top) | (a->bottom - b->bottom); } /* Returns n for a match, returns -1 for no match */ int decor_match_pixmap (long *data, int size, Pixmap *pixmap, decor_extents_t *frame, decor_extents_t *border, decor_extents_t *max_frame, decor_extents_t *max_border, int min_width, int min_height, unsigned int frame_type, unsigned int frame_state, unsigned int frame_actions, decor_quad_t *quad, unsigned int n_quad) { int n = decor_property_get_num (data); unsigned int i = 0; for (; i < n; ++i) { Pixmap cPixmap; decor_extents_t cFrame, cBorder, cMax_frame, cMax_border; int cMin_width, cMin_height; int q; unsigned int cFrame_type, cFrame_state, cFrame_actions, cNQuad; decor_quad_t cQuad[N_QUADS_MAX]; cNQuad = decor_pixmap_property_to_quads (data, i, size, &cPixmap, &cFrame, &cBorder, &cMax_frame, &cMax_border, &cMin_width, &cMin_height, &cFrame_type, &cFrame_state, &cFrame_actions, cQuad); if (cPixmap != *pixmap) continue; if (decor_extents_cmp (&cFrame, frame) || decor_extents_cmp (&cBorder, border) || decor_extents_cmp (&cMax_frame, max_frame) || decor_extents_cmp (&cMax_border, max_border)) continue; if (cFrame_type != frame_type || cFrame_state != frame_state || cFrame_actions != frame_actions || cMin_width != min_width || cMin_height != min_height) continue; if (cNQuad != n_quad) continue; q = 0; while (q < n_quad && !decor_quad_cmp (&cQuad[q], &quad[q])) ++q; if (q < n_quad) continue; return n; } return -1; } int decor_window_property (long *data, unsigned int n, int size, decor_extents_t *input, decor_extents_t *max_input, int *min_width, int *min_height, unsigned int *frame_type, unsigned int *frame_state, unsigned int *frame_actions) { if (decor_property_get_version (data) != decor_version ()) return 0; if (decor_property_get_type (data) != WINDOW_DECORATION_TYPE_WINDOW) return 0; if (size < PROP_HEADER_SIZE + n * WINDOW_PROP_SIZE) return 0; data += PROP_HEADER_SIZE + n * WINDOW_PROP_SIZE; input->left = *data++; input->right = *data++; input->top = *data++; input->bottom = *data++; max_input->left = *data++; max_input->right = *data++; max_input->top = *data++; max_input->bottom = *data++; *min_width = *data++; *min_height = *data++; *frame_type = *data++; *frame_state = *data++; *frame_actions = *data++; return 1; } static int add_blur_boxes (long *data, BoxPtr box, int n_box, int width, int height, int gravity, int offset) { int x1, y1, x2, y2; int more_gravity; int n = n_box; while (n--) { x1 = box->x1; y1 = box->y1; x2 = box->x2; y2 = box->y2; if (gravity & (GRAVITY_NORTH | GRAVITY_SOUTH)) { if (x1 > offset) { more_gravity = GRAVITY_EAST; x1 -= width; } else { more_gravity = GRAVITY_WEST; } } else { if (y1 > offset) { more_gravity = GRAVITY_SOUTH; y1 -= height; } else { more_gravity = GRAVITY_NORTH; } } *data++ = gravity | more_gravity; *data++ = x1; *data++ = y1; if (gravity & (GRAVITY_NORTH | GRAVITY_SOUTH)) { if (x2 > offset) { more_gravity = GRAVITY_EAST; x2 -= width; } else { more_gravity = GRAVITY_WEST; } } else { if (y2 > offset) { more_gravity = GRAVITY_SOUTH; y2 -= height; } else { more_gravity = GRAVITY_NORTH; } } *data++ = gravity | more_gravity; *data++ = x2; *data++ = y2; ++box; } return n_box * 6; } void decor_region_to_blur_property (long *data, int threshold, int filter, int width, int height, Region top_region, int top_offset, Region bottom_region, int bottom_offset, Region left_region, int left_offset, Region right_region, int right_offset) { *data++ = threshold; *data++ = filter; if (top_region) data += add_blur_boxes (data, top_region->rects, top_region->numRects, width, height, GRAVITY_NORTH, top_offset); if (bottom_region) data += add_blur_boxes (data, bottom_region->rects, bottom_region->numRects, width, height, GRAVITY_SOUTH, bottom_offset); if (left_region) data += add_blur_boxes (data, left_region->rects, left_region->numRects, width, height, GRAVITY_WEST, left_offset); if (right_region) data += add_blur_boxes (data, right_region->rects, right_region->numRects, width, height, GRAVITY_EAST, right_offset); } void decor_apply_gravity (int gravity, int x, int y, int width, int height, int *return_x, int *return_y) { if (gravity & GRAVITY_EAST) { x += width; *return_x = MAX (0, x); } else if (gravity & GRAVITY_WEST) { *return_x = MIN (width, x); } else { x += width / 2; x = MAX (0, x); x = MIN (width, x); *return_x = x; } if (gravity & GRAVITY_SOUTH) { y += height; *return_y = MAX (0, y); } else if (gravity & GRAVITY_NORTH) { *return_y = MIN (height, y); } else { y += height / 2; y = MAX (0, y); y = MIN (height, y); *return_y = y; } } int decor_set_vert_quad_row (decor_quad_t *q, int top, int top_corner, int bottom, int bottom_corner, int left, int right, int gravity, int height, int splitY, int splitGravity, double x0, double y0, int rotation) { int nQuad = 0; q->p1.x = left; q->p1.y = -top; q->p1.gravity = gravity | GRAVITY_NORTH; q->p2.x = right; q->p2.y = splitY; q->p2.gravity = gravity | splitGravity; q->max_width = SHRT_MAX; q->max_height = top + top_corner; q->align = ALIGN_TOP; q->clamp = CLAMP_VERT; q->stretch = 0; q->m.x0 = x0; q->m.y0 = y0; if (rotation) { q->m.xx = 0.0; q->m.xy = 1.0; q->m.yx = 1.0; q->m.yy = 0.0; } else { q->m.xx = 1.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 1.0; } ++q; ++nQuad; q->p1.x = left; q->p1.y = top_corner; q->p1.gravity = gravity | GRAVITY_NORTH; q->p2.x = right; q->p2.y = -bottom_corner; q->p2.gravity = gravity | GRAVITY_SOUTH; q->max_width = SHRT_MAX; q->max_height = SHRT_MAX; q->align = 0; q->clamp = CLAMP_VERT; q->stretch = 0; if (rotation) { q->m.xx = 0.0; q->m.xy = 0.0; q->m.yx = 1.0; q->m.yy = 0.0; q->m.x0 = x0 + top + top_corner; q->m.y0 = y0; } else { q->m.xx = 1.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 0.0; q->m.x0 = x0; q->m.y0 = y0 + top + top_corner; } ++q; ++nQuad; q->p1.x = left; q->p1.y = splitY; q->p1.gravity = gravity | splitGravity; q->p2.x = right; q->p2.y = bottom; q->p2.gravity = gravity | GRAVITY_SOUTH; q->max_width = SHRT_MAX; q->max_height = bottom_corner + bottom; q->align = ALIGN_BOTTOM; q->clamp = CLAMP_VERT; q->stretch = 0; if (rotation) { q->m.xx = 0.0; q->m.xy = 1.0; q->m.yx = 1.0; q->m.yy = 0.0; q->m.x0 = x0 + height; q->m.y0 = y0; } else { q->m.xx = 1.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 1.0; q->m.x0 = x0; q->m.y0 = y0 + height; } ++nQuad; return nQuad; } int decor_set_horz_quad_line (decor_quad_t *q, int left, int left_corner, int right, int right_corner, int top, int bottom, int gravity, int width, int splitX, int splitGravity, double x0, double y0) { int nQuad = 0; q->p1.x = -left; q->p1.y = top; q->p1.gravity = gravity | GRAVITY_WEST; q->p2.x = splitX; q->p2.y = bottom; q->p2.gravity = gravity | splitGravity; q->max_width = left + left_corner; q->max_height = SHRT_MAX; q->align = ALIGN_LEFT; q->clamp = 0; q->stretch = 0; q->m.xx = 1.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 1.0; q->m.x0 = x0; q->m.y0 = y0; ++q; ++nQuad; q->p1.x = left_corner; q->p1.y = top; q->p1.gravity = gravity | GRAVITY_WEST; q->p2.x = -right_corner; q->p2.y = bottom; q->p2.gravity = gravity | GRAVITY_EAST; q->max_width = SHRT_MAX; q->max_height = SHRT_MAX; q->align = 0; q->clamp = 0; q->stretch = 0; q->m.xx = 0.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 1.0; q->m.x0 = x0 + left + left_corner; q->m.y0 = y0; ++q; ++nQuad; q->p1.x = splitX; q->p1.y = top; q->p1.gravity = gravity | splitGravity; q->p2.x = right; q->p2.y = bottom; q->p2.gravity = gravity | GRAVITY_EAST; q->max_width = right_corner + right; q->max_height = SHRT_MAX; q->align = ALIGN_RIGHT; q->clamp = 0; q->stretch = 0; q->m.xx = 1.0; q->m.xy = 0.0; q->m.yx = 0.0; q->m.yy = 1.0; q->m.x0 = x0 + width; q->m.y0 = y0; ++nQuad; return nQuad; } int decor_set_lSrS_window_quads (decor_quad_t *q, decor_context_t *c, decor_layout_t *l) { int lh, rh, splitY, n, nQuad = 0; splitY = (c->top_corner_space - c->bottom_corner_space) / 2; if (l->rotation) { lh = l->left.x2 - l->left.x1; rh = l->right.x2 - l->right.x1; } else { lh = l->left.y2 - l->left.y1; rh = l->right.y2 - l->right.y1; } /* left quads */ n = decor_set_vert_quad_row (q, 0, c->top_corner_space, 0, c->bottom_corner_space, -c->left_space, 0, GRAVITY_WEST, lh, splitY, 0, l->left.x1, l->left.y1, l->rotation); q += n; nQuad += n; /* right quads */ n = decor_set_vert_quad_row (q, 0, c->top_corner_space, 0, c->bottom_corner_space, 0, c->right_space, GRAVITY_EAST, rh, splitY, 0, l->right.x1, l->right.y1, l->rotation); nQuad += n; return nQuad; } int decor_set_lSrStSbS_window_quads (decor_quad_t *q, decor_context_t *c, decor_layout_t *l) { int splitX, n, nQuad = 0; splitX = (c->left_corner_space - c->right_corner_space) / 2; /* top quads */ n = decor_set_horz_quad_line (q, c->left_space, c->left_corner_space, c->right_space, c->right_corner_space, -c->top_space, 0, GRAVITY_NORTH, l->top.x2 - l->top.x1, splitX, 0, l->top.x1, l->top.y1); q += n; nQuad += n; n = decor_set_lSrS_window_quads (q, c, l); q += n; nQuad += n; /* bottom quads */ n = decor_set_horz_quad_line (q, c->left_space, c->left_corner_space, c->right_space, c->right_corner_space, 0, c->bottom_space, GRAVITY_SOUTH, l->bottom.x2 - l->bottom.x1, splitX, 0, l->bottom.x1, l->bottom.y1); nQuad += n; return nQuad; } int decor_set_lSrStXbS_window_quads (decor_quad_t *q, decor_context_t *c, decor_layout_t *l, int top_stretch_offset) { int splitX, n, nQuad = 0; int top_left, top_right; splitX = (c->left_corner_space - c->right_corner_space) / 2; top_left = top_stretch_offset; top_right = l->top.x2 - l->top.x1 - c->left_space - c->right_space - top_left - 1; /* top quads */ n = decor_set_horz_quad_line (q, c->left_space, top_left, c->right_space, top_right, -c->top_space, 0, GRAVITY_NORTH, l->top.x2 - l->top.x1, -top_right, GRAVITY_EAST, l->top.x1, l->top.y1); q += n; nQuad += n; n = decor_set_lSrS_window_quads (q, c, l); q += n; nQuad += n; /* bottom quads */ n = decor_set_horz_quad_line (q, c->left_space, c->left_corner_space, c->right_space, c->right_corner_space, 0, c->bottom_space, GRAVITY_SOUTH, l->bottom.x2 - l->bottom.x1, splitX, 0, l->bottom.x1, l->bottom.y1); nQuad += n; return nQuad; } int decor_set_lSrStSbX_window_quads (decor_quad_t *q, decor_context_t *c, decor_layout_t *l, int bottom_stretch_offset) { int splitX, n, nQuad = 0; int bottom_left, bottom_right; splitX = (c->left_corner_space - c->right_corner_space) / 2; bottom_left = bottom_stretch_offset; bottom_right = l->bottom.x2 - l->bottom.x1 - c->left_space - c->right_space - bottom_left - 1; /* top quads */ n = decor_set_horz_quad_line (q, c->left_space, c->left_corner_space, c->right_space, c->right_corner_space, -c->top_space, 0, GRAVITY_NORTH, l->top.x2 - l->top.x1, splitX, 0, l->top.x1, l->top.y1); q += n; nQuad += n; n = decor_set_lSrS_window_quads (q, c, l); q += n; nQuad += n; /* bottom quads */ n = decor_set_horz_quad_line (q, c->left_space, bottom_left, c->right_space, bottom_right, 0, c->bottom_space, GRAVITY_SOUTH, l->bottom.x2 - l->bottom.x1, -bottom_right, GRAVITY_EAST, l->bottom.x1, l->bottom.y1); nQuad += n; return nQuad; } int decor_set_lXrXtXbX_window_quads (decor_quad_t *q, decor_context_t *c, decor_layout_t *l, int left_stretch_offset, int right_stretch_offset, int top_stretch_offset, int bottom_stretch_offset) { int lh, rh, n, nQuad = 0; int left_top, left_bottom; int right_top, right_bottom; int top_left, top_right; int bottom_left, bottom_right; top_left = top_stretch_offset; top_right = l->top.x2 - l->top.x1 - c->left_space - c->right_space - top_left - 1; bottom_left = bottom_stretch_offset; bottom_right = l->bottom.x2 - l->bottom.x1 - c->left_space - c->right_space - bottom_left - 1; if (l->rotation) { lh = l->left.x2 - l->left.x1; rh = l->right.x2 - l->right.x1; } else { lh = l->left.y2 - l->left.y1; rh = l->right.y2 - l->right.y1; } left_top = left_stretch_offset; left_bottom = lh - left_top - 1; right_top = right_stretch_offset; right_bottom = rh - right_top - 1; /* top quads */ n = decor_set_horz_quad_line (q, c->left_space, top_left, c->right_space, top_right, -c->top_space, 0, GRAVITY_NORTH, l->top.x2 - l->top.x1, -top_right, GRAVITY_EAST, l->top.x1, l->top.y1); q += n; nQuad += n; /* left quads */ n = decor_set_vert_quad_row (q, 0, left_top, 0, left_bottom, -c->left_space, 0, GRAVITY_WEST, lh, -left_bottom, GRAVITY_SOUTH, l->left.x1, l->left.y1, l->rotation); q += n; nQuad += n; /* right quads */ n = decor_set_vert_quad_row (q, 0, right_top, 0, right_bottom, 0, c->right_space, GRAVITY_EAST, rh, -right_bottom, GRAVITY_SOUTH, l->right.x1, l->right.y1, l->rotation); q += n; nQuad += n; /* bottom quads */ n = decor_set_horz_quad_line (q, c->left_space, bottom_left, c->right_space, bottom_right, 0, c->bottom_space, GRAVITY_SOUTH, l->bottom.x2 - l->bottom.x1, -bottom_right, GRAVITY_EAST, l->bottom.x1, l->bottom.y1); nQuad += n; return nQuad; } #if INT_MAX != LONG_MAX static int errors; static int error_handler (Display *xdisplay, XErrorEvent *event) { ++errors; return 0; } /* XRenderSetPictureFilter used to be broken on LP64. This * works with either the broken or fixed version. */ static void XRenderSetPictureFilter_wrapper (Display *dpy, Picture picture, char *filter, XFixed *params, int nparams) { int (*old) (Display *, XErrorEvent *); errors = 0; old = XSetErrorHandler (error_handler); XRenderSetPictureFilter (dpy, picture, filter, params, nparams); XSync (dpy, False); XSetErrorHandler (old); if (errors) { long *long_params = malloc (sizeof (long) * nparams); int i; for (i = 0; i < nparams; ++i) long_params[i] = params[i]; XRenderSetPictureFilter (dpy, picture, filter, (XFixed *) long_params, nparams); free (long_params); } } #define XRenderSetPictureFilter XRenderSetPictureFilter_wrapper #endif static void set_picture_transform (Display *xdisplay, Picture p, int dx, int dy) { XTransform transform = { { { 1 << 16, 0, -dx << 16 }, { 0, 1 << 16, -dy << 16 }, { 0, 0, 1 << 16 }, } }; XRenderSetPictureTransform (xdisplay, p, &transform); } static void set_picture_clip (Display *xdisplay, Picture p, int width, int height, int clipX1, int clipY1, int clipX2, int clipY2) { XRectangle clip[4]; clip[0].x = 0; clip[0].y = 0; clip[0].width = width; clip[0].height = clipY1; clip[1].x = 0; clip[1].y = clipY2; clip[1].width = width; clip[1].height = height - clipY2; clip[2].x = 0; clip[2].y = clipY1; clip[2].width = clipX1; clip[2].height = clipY2 - clipY1; clip[3].x = clipX2; clip[3].y = clipY1; clip[3].width = width - clipX2; clip[3].height = clipY2 - clipY1; XRenderSetPictureClipRectangles (xdisplay, p, 0, 0, clip, 4); } static void set_no_picture_clip (Display *xdisplay, Picture p) { XRectangle clip; clip.x = 0; clip.y = 0; clip.width = SHRT_MAX; clip.height = SHRT_MAX; XRenderSetPictureClipRectangles (xdisplay, p, 0, 0, &clip, 1); } static XFixed * create_gaussian_kernel (double radius, double sigma, double alpha, int *r_size) { XFixed *params; double *amp, scale, x_scale, fx, sum; int size, half_size, x, i, n; scale = 1.0f / (2.0f * M_PI * sigma * sigma); half_size = alpha + 0.5f; if (half_size == 0) half_size = 1; size = half_size * 2 + 1; x_scale = 2.0f * radius / size; if (size < 3) return NULL; n = size; amp = malloc (sizeof (double) * n); if (!amp) return NULL; n += 2; params = malloc (sizeof (XFixed) * n); if (!params) { free (amp); return NULL; } i = 0; sum = 0.0f; for (x = 0; x < size; ++x) { fx = x_scale * (x - half_size); amp[i] = scale * exp ((-1.0f * (fx * fx)) / (2.0f * sigma * sigma)); sum += amp[i]; ++i; } /* normalize */ if (sum != 0.0) sum = 1.0 / sum; params[0] = params[1] = 0; for (i = 2; i < n; ++i) params[i] = XDoubleToFixed (amp[i - 2] * sum); free (amp); *r_size = size; return params; } #define SIGMA(r) ((r) / 2.0) #define ALPHA(r) (r) decor_shadow_t * decor_shadow_create (Display *xdisplay, Screen *screen, int width, int height, int left, int right, int top, int bottom, int solid_left, int solid_right, int solid_top, int solid_bottom, decor_shadow_options_t *opt, decor_context_t *c, decor_draw_func_t draw, void *closure) { static XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; XRenderPictFormat *format; Pixmap pixmap; Picture src, dst, tmp; XFixed opacity, *params; XFilters *filters; char *filter = NULL; int size, n_params = 0; XRenderColor color; int shadow_offset_x; int shadow_offset_y; Pixmap d_pixmap; int d_width; int d_height; Window xroot = screen->root; decor_shadow_t *shadow; int clipX1, clipY1, clipX2, clipY2; shadow = malloc (sizeof (decor_shadow_t)); if (!shadow) return NULL; shadow->ref_count = 1; shadow->pixmap = 0; shadow->picture = 0; shadow->width = 0; shadow->height = 0; shadow_offset_x = opt->shadow_offset_x; shadow_offset_y = opt->shadow_offset_y; /* compute a gaussian convolution kernel */ params = create_gaussian_kernel (opt->shadow_radius, SIGMA (opt->shadow_radius), ALPHA (opt->shadow_radius), &size); if (!params) shadow_offset_x = shadow_offset_y = size = 0; if (opt->shadow_radius <= 0.0 && shadow_offset_x == 0 && shadow_offset_y == 0) size = 0; n_params = size + 2; size = size / 2; c->extents.left = left; c->extents.right = right; c->extents.top = top; c->extents.bottom = bottom; c->left_space = left + size - shadow_offset_x; c->right_space = right + size + shadow_offset_x; c->top_space = top + size - shadow_offset_y; c->bottom_space = bottom + size + shadow_offset_y; c->left_space = MAX (left, c->left_space); c->right_space = MAX (right, c->right_space); c->top_space = MAX (top, c->top_space); c->bottom_space = MAX (bottom, c->bottom_space); c->left_corner_space = MAX (1, size - solid_left + shadow_offset_x); c->right_corner_space = MAX (1, size - solid_right - shadow_offset_x); c->top_corner_space = MAX (1, size - solid_top + shadow_offset_y); c->bottom_corner_space = MAX (1, size - solid_bottom - shadow_offset_y); width = MAX (width, c->left_corner_space + c->right_corner_space); height = MAX (height, c->top_corner_space + c->bottom_corner_space); width = MAX (1, width); height = MAX (1, height); d_width = c->left_space + width + c->right_space; d_height = c->top_space + height + c->bottom_space; /* all pixmaps are ARGB32 */ format = XRenderFindStandardFormat (xdisplay, PictStandardARGB32); /* no shadow */ if (size <= 0) { if (params) free (params); return shadow; } pixmap = XCreatePixmap (xdisplay, xroot, d_width, d_height, 32); if (!pixmap) { free (params); return shadow; } /* query server for convolution filter */ filters = XRenderQueryFilters (xdisplay, pixmap); if (filters) { int i; for (i = 0; i < filters->nfilter; ++i) { if (strcmp (filters->filter[i], FilterConvolution) == 0) { filter = (char *) FilterConvolution; break; } } XFree (filters); } if (!filter) { XFreePixmap (xdisplay, pixmap); free (params); return shadow; } /* create pixmap for temporary decorations */ d_pixmap = XCreatePixmap (xdisplay, xroot, d_width, d_height, 32); if (!d_pixmap) { XFreePixmap (xdisplay, pixmap); free (params); return shadow; } src = XRenderCreateSolidFill (xdisplay, &white); dst = XRenderCreatePicture (xdisplay, d_pixmap, format, 0, NULL); tmp = XRenderCreatePicture (xdisplay, pixmap, format, 0, NULL); /* draw decoration */ (*draw) (xdisplay, d_pixmap, dst, d_width, d_height, c, closure); /* first pass */ params[0] = (n_params - 2) << 16; params[1] = 1 << 16; clipX1 = c->left_space + size; clipY1 = c->top_space + size; clipX2 = d_width - c->right_space - size; clipY2 = d_height - c->bottom_space - size; if (clipX1 < clipX2 && clipY1 < clipY2) set_picture_clip (xdisplay, tmp, d_width, d_height, clipX1, clipY1, clipX2, clipY2); set_picture_transform (xdisplay, dst, shadow_offset_x, 0); XRenderSetPictureFilter (xdisplay, dst, filter, params, n_params); XRenderComposite (xdisplay, PictOpSrc, src, dst, tmp, 0, 0, 0, 0, 0, 0, d_width, d_height); set_no_picture_clip (xdisplay, tmp); XRenderFreePicture (xdisplay, src); /* second pass */ params[0] = 1 << 16; params[1] = (n_params - 2) << 16; opacity = XDoubleToFixed (opt->shadow_opacity); if (opacity < (1 << 16)) { /* apply opacity as shadow color if less than 1.0 */ color.red = (opt->shadow_color[0] * opacity) >> 16; color.green = (opt->shadow_color[1] * opacity) >> 16; color.blue = (opt->shadow_color[2] * opacity) >> 16; color.alpha = opacity; opacity = 1 << 16; } else { /* shadow color */ color.red = opt->shadow_color[0]; color.green = opt->shadow_color[1]; color.blue = opt->shadow_color[2]; color.alpha = 0xffff; } src = XRenderCreateSolidFill (xdisplay, &color); clipX1 = c->left_space; clipY1 = c->top_space; clipX2 = d_width - c->right_space; clipY2 = d_height - c->bottom_space; if (clipX1 < clipX2 && clipY1 < clipY2) set_picture_clip (xdisplay, dst, d_width, d_height, clipX1, clipY1, clipX2, clipY2); set_picture_transform (xdisplay, tmp, 0, shadow_offset_y); XRenderSetPictureFilter (xdisplay, tmp, filter, params, n_params); XRenderComposite (xdisplay, PictOpSrc, src, tmp, dst, 0, 0, 0, 0, 0, 0, d_width, d_height); set_no_picture_clip (xdisplay, dst); XRenderFreePicture (xdisplay, src); if (opacity != (1 << 16)) { XFixed p[3]; p[0] = 1 << 16; p[1] = 1 << 16; p[2] = opacity; if (clipX1 < clipX2 && clipY1 < clipY2) set_picture_clip (xdisplay, tmp, d_width, d_height, clipX1, clipY1, clipX2, clipY2); /* apply opacity */ set_picture_transform (xdisplay, dst, 0, 0); XRenderSetPictureFilter (xdisplay, dst, filter, p, 3); XRenderComposite (xdisplay, PictOpSrc, dst, None, tmp, 0, 0, 0, 0, 0, 0, d_width, d_height); XFreePixmap (xdisplay, d_pixmap); shadow->pixmap = pixmap; } else { XFreePixmap (xdisplay, pixmap); shadow->pixmap = d_pixmap; } XRenderFreePicture (xdisplay, tmp); XRenderFreePicture (xdisplay, dst); shadow->picture = XRenderCreatePicture (xdisplay, shadow->pixmap, format, 0, NULL); shadow->width = d_width; shadow->height = d_height; free (params); return shadow; } void decor_shadow_destroy (Display *xdisplay, decor_shadow_t *shadow) { --shadow->ref_count; if (shadow->ref_count) return; if (shadow->picture) XRenderFreePicture (xdisplay, shadow->picture); if (shadow->pixmap) XFreePixmap (xdisplay, shadow->pixmap); free (shadow); } void decor_shadow_reference (decor_shadow_t *shadow) { ++shadow->ref_count; } void decor_draw_simple (Display *xdisplay, Pixmap pixmap, Picture picture, int width, int height, decor_context_t *c, void *closure) { static XRenderColor clear = { 0x0000, 0x0000, 0x0000, 0x0000 }; static XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; XRenderFillRectangle (xdisplay, PictOpSrc, picture, &clear, 0, 0, width, height); XRenderFillRectangle (xdisplay, PictOpSrc, picture, &white, c->left_space - c->extents.left, c->top_space - c->extents.top, width - c->left_space - c->right_space + c->extents.left + c->extents.right, height - c->top_space - c->bottom_space + c->extents.top + c->extents.bottom); } void decor_get_default_layout (decor_context_t *c, int width, int height, decor_layout_t *layout) { width = MAX (width, c->left_corner_space + c->right_corner_space); height = MAX (height, c->top_corner_space + c->bottom_corner_space); width += c->left_space + c->right_space; layout->top.x1 = 0; layout->top.y1 = 0; layout->top.x2 = width; layout->top.y2 = c->top_space; layout->top.pad = 0; layout->left.x1 = 0; layout->left.y1 = c->top_space; layout->left.x2 = c->left_space; layout->left.y2 = c->top_space + height; layout->left.pad = 0; layout->right.x1 = width - c->right_space; layout->right.y1 = c->top_space; layout->right.x2 = width; layout->right.y2 = c->top_space + height; layout->right.pad = 0; layout->bottom.x1 = 0; layout->bottom.y1 = height + c->top_space; layout->bottom.x2 = width; layout->bottom.y2 = height + c->top_space + c->bottom_space; layout->bottom.pad = 0; layout->width = width; layout->height = height + c->top_space + c->bottom_space; layout->rotation = 0; } void decor_get_best_layout (decor_context_t *c, int width, int height, decor_layout_t *layout) { int y; /* use default layout when no left and right extents */ if (c->extents.left == 0 && c->extents.right == 0) { decor_get_default_layout (c, width, 1, layout); return; } width = MAX (width, c->left_corner_space + c->right_corner_space); height = MAX (height, c->top_corner_space + c->bottom_corner_space); width += c->left_space + c->right_space; if (width >= (height + 2)) { int max; layout->width = width; layout->top.x1 = 0; layout->top.y1 = 0; layout->top.x2 = width; layout->top.y2 = c->top_space; y = c->top_space; max = MAX (c->left_space, c->right_space); if (max < height) { layout->rotation = 1; y += 2; layout->top.pad = PAD_BOTTOM; layout->bottom.pad = PAD_TOP; layout->left.pad = PAD_TOP | PAD_BOTTOM | PAD_LEFT | PAD_RIGHT; layout->right.pad = PAD_TOP | PAD_BOTTOM | PAD_LEFT | PAD_RIGHT; layout->left.x1 = 1; layout->left.y1 = y; layout->left.x2 = 1 + height; layout->left.y2 = y + c->left_space; if ((height + 2) <= (width / 2)) { layout->right.x1 = height + 3; layout->right.y1 = y; layout->right.x2 = height + 3 + height; layout->right.y2 = y + c->right_space; y += max + 2; } else { y += c->left_space + 2; layout->right.x1 = 1; layout->right.y1 = y; layout->right.x2 = 1 + height; layout->right.y2 = y + c->right_space; y += c->right_space + 2; } } else { layout->rotation = 0; layout->top.pad = 0; layout->bottom.pad = 0; layout->left.pad = 0; layout->right.pad = 0; layout->left.x1 = 0; layout->left.y1 = y; layout->left.x2 = c->left_space; layout->left.y2 = y + height; layout->right.x1 = width - c->right_space; layout->right.y1 = y; layout->right.x2 = width; layout->right.y2 = y + height; y += height; } layout->bottom.x1 = 0; layout->bottom.y1 = y; layout->bottom.x2 = width; layout->bottom.y2 = y + c->bottom_space; y += c->bottom_space; } else { layout->rotation = 1; layout->left.pad = PAD_TOP | PAD_BOTTOM | PAD_LEFT | PAD_RIGHT; layout->right.pad = PAD_TOP | PAD_BOTTOM | PAD_LEFT | PAD_RIGHT; layout->top.x1 = 0; layout->top.y1 = 0; layout->top.x2 = width; layout->top.y2 = c->top_space; if (((width * 2) + 3) <= (height + 2)) { layout->width = height + 2; layout->top.pad = PAD_BOTTOM | PAD_RIGHT; layout->bottom.pad = PAD_TOP | PAD_BOTTOM | PAD_RIGHT | PAD_LEFT; layout->bottom.x1 = width + 2; layout->bottom.y1 = 1; layout->bottom.x2 = width + 2 + width; layout->bottom.y2 = 1 + c->bottom_space; y = MAX (c->top_space, 1 + c->bottom_space) + 2; layout->left.x1 = 1; layout->left.y1 = y; layout->left.x2 = 1 + height; layout->left.y2 = y + c->left_space; y += c->left_space + 2; layout->right.x1 = 1; layout->right.y1 = y; layout->right.x2 = 1 + height; layout->right.y2 = y + c->right_space; y += c->right_space; } else { layout->width = height + 2; layout->top.pad = PAD_BOTTOM | PAD_RIGHT; layout->bottom.pad = PAD_TOP | PAD_RIGHT; y = c->top_space + 2; layout->left.x1 = 1; layout->left.y1 = y; layout->left.x2 = 1 + height; layout->left.y2 = y + c->left_space; y += c->left_space + 2; layout->right.x1 = 1; layout->right.y1 = y; layout->right.x2 = 1 + height; layout->right.y2 = y + c->right_space; y += c->right_space + 2; layout->bottom.x1 = 0; layout->bottom.y1 = y; layout->bottom.x2 = width; layout->bottom.y2 = y + c->bottom_space; y += c->bottom_space; } } layout->height = y; } static XTransform xident = { { { 1 << 16, 0, 0 }, { 0, 1 << 16, 0 }, { 0, 0, 1 << 16 }, } }; void decor_fill_picture_extents_with_shadow (Display *xdisplay, decor_shadow_t *shadow, decor_context_t *context, Picture picture, decor_layout_t *layout) { int w, h, left, right, top, bottom, width, height; if (!shadow->picture) return; width = layout->top.x2 - layout->top.x1; if (layout->rotation) height = layout->left.x2 - layout->left.x1; else height = layout->left.y2 - layout->left.y1; height += context->top_space + context->bottom_space; left = context->left_space + context->left_corner_space; right = context->right_space + context->right_corner_space; top = context->top_space + context->top_corner_space; bottom = context->bottom_space + context->bottom_corner_space; if (width - left - right < 0) { left = width / 2; right = width - left; } if (height - top - bottom < 0) { top = height / 2; bottom = height - top; } w = width - left - right; h = height - top - bottom; /* top left */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->top.x1, layout->top.y1, left, context->top_space); /* top right */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, shadow->width - right, 0, 0, 0, layout->top.x2 - right, layout->top.y1, right, context->top_space); /* bottom left */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, shadow->height - context->bottom_space, 0, 0, layout->bottom.x1, layout->bottom.y1, left, context->bottom_space); /* bottom right */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, shadow->width - right, shadow->height - context->bottom_space, 0, 0, layout->bottom.x2 - right, layout->bottom.y1, right, context->bottom_space); if (w > 0) { int sw = shadow->width - left - right; int sx = left; if (sw != w) { XTransform t = { { { (sw << 16) / w, 0, left << 16 }, { 0, 1 << 16, 0 }, { 0, 0, 1 << 16 }, } }; sx = 0; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); } /* top */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, sx, 0, 0, 0, layout->top.x1 + left, layout->top.y1, w, context->top_space); /* bottom */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, sx, shadow->height - context->bottom_space, 0, 0, layout->bottom.x1 + left, layout->bottom.y1, w, context->bottom_space); if (sw != w) XRenderSetPictureTransform (xdisplay, shadow->picture, &xident); } if (layout->rotation) { XTransform t = { { { 0, 1 << 16, 0 }, { 1 << 16, 0, 0 }, { 0, 0, 1 << 16 } } }; t.matrix[1][2] = context->top_space << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* left top */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->left.x1, layout->left.y1, top - context->top_space, context->left_space); t.matrix[0][2] = (shadow->width - context->right_space) << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* right top */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->right.x1, layout->right.y1, top - context->top_space, context->right_space); XRenderSetPictureTransform (xdisplay, shadow->picture, &xident); } else { /* left top */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, context->top_space, 0, 0, layout->left.x1, layout->left.y1, context->left_space, top - context->top_space); /* right top */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, shadow->width - context->right_space, context->top_space, 0, 0, layout->right.x1, layout->right.y1, context->right_space, top - context->top_space); } if (layout->rotation) { XTransform t = { { { 0, 1 << 16, 0 }, { 1 << 16, 0, 0 }, { 0, 0, 1 << 16 } } }; t.matrix[1][2] = (shadow->height - bottom) << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* left bottom */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->left.x2 - (bottom - context->bottom_space), layout->left.y1, bottom - context->bottom_space, context->left_space); t.matrix[0][2] = (shadow->width - context->right_space) << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* right bottom */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->right.x2 - (bottom - context->bottom_space), layout->right.y1, bottom - context->bottom_space, context->right_space); XRenderSetPictureTransform (xdisplay, shadow->picture, &xident); } else { /* left bottom */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, shadow->height - bottom, 0, 0, layout->left.x1, layout->left.y2 - (bottom - context->bottom_space), context->left_space, bottom - context->bottom_space); /* right bottom */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, shadow->width - context->right_space, shadow->height - bottom, 0, 0, layout->right.x1, layout->right.y2 - (bottom - context->bottom_space), context->right_space, bottom - context->bottom_space); } if (h > 0) { int sh = shadow->height - top - bottom; if (layout->rotation) { XTransform t = { { { 0, 1 << 16, 0 }, { (sh << 16) / h, 0, 0 }, { 0, 0, 1 << 16 } } }; t.matrix[1][2] = top << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* left */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->left.x1 + (top - context->top_space), layout->left.y1, h, context->left_space); t.matrix[0][2] = (shadow->width - context->right_space) << 16; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); /* right */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, 0, 0, 0, layout->right.x1 + (top - context->top_space), layout->right.y1, h, context->right_space); XRenderSetPictureTransform (xdisplay, shadow->picture, &xident); } else { int sy = top; if (sh != h) { XTransform t = { { { 1 << 16, 0, 0 }, { 0, (sh << 16) / h, top << 16 }, { 0, 0, 1 << 16 }, } }; sy = 0; XRenderSetPictureTransform (xdisplay, shadow->picture, &t); } /* left */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, 0, sy, 0, 0, layout->left.x1, layout->left.y1 + (top - context->top_space), context->left_space, h); /* right */ XRenderComposite (xdisplay, PictOpSrc, shadow->picture, 0, picture, shadow->width - context->right_space, sy, 0, 0, layout->right.x2 - context->right_space, layout->right.y1 + (top - context->top_space), context->right_space, h); if (sh != h) XRenderSetPictureTransform (xdisplay, shadow->picture, &xident); } } } static void _decor_pad_border_picture (Display *xdisplay, Picture dst, decor_box_t *box) { int x1, y1, x2, y2; x1 = box->x1; y1 = box->y1; x2 = box->x2; y2 = box->y2; if (box->pad & PAD_TOP) { XRenderComposite (xdisplay, PictOpSrc, dst, None, dst, x1, y1, 0, 0, x1, y1 - 1, x2 - x1, 1); --y1; } if (box->pad & PAD_BOTTOM) { XRenderComposite (xdisplay, PictOpSrc, dst, None, dst, x1, y2 - 1, 0, 0, x1, y2, x2 - x1, 1); ++y2; } if (box->pad & PAD_LEFT) { XRenderComposite (xdisplay, PictOpSrc, dst, None, dst, x1, y1, 0, 0, x1 - 1, y1, 1, y2 - y1); } if (box->pad & PAD_RIGHT) { XRenderComposite (xdisplay, PictOpSrc, dst, None, dst, x2 - 1, y1, 0, 0, x2, y1, 1, y2 - y1); } } #ifndef HAVE_XRENDER_0_9_3 /* XRenderCreateLinearGradient and XRenderCreateRadialGradient used to be * broken. Flushing Xlib's output buffer before calling one of these * functions will avoid this specific issue. */ static Picture XRenderCreateLinearGradient_wrapper (Display *xdisplay, const XLinearGradient *gradient, const XFixed *stops, const XRenderColor *colors, int nStops) { XFlush (xdisplay); return XRenderCreateLinearGradient (xdisplay, gradient, stops, colors, nStops); } static Picture XRenderCreateRadialGradient_wrapper (Display *xdisplay, const XRadialGradient *gradient, const XFixed *stops, const XRenderColor *colors, int nStops) { XFlush (xdisplay); return XRenderCreateRadialGradient (xdisplay, gradient, stops, colors, nStops); } #define XRenderCreateLinearGradient XRenderCreateLinearGradient_wrapper #define XRenderCreateRadialGradient XRenderCreateRadialGradient_wrapper #endif static void _decor_blend_horz_border_picture (Display *xdisplay, decor_context_t *context, Picture src, int xSrc, int ySrc, Picture dst, decor_layout_t *layout, Region region, unsigned short alpha, int shade_alpha, int x1, int y1, int x2, int y2, int dy, int direction, int ignore_src_alpha) { XRenderColor color[3] = { { 0xffff, 0xffff, 0xffff, 0xffff }, { alpha, alpha, alpha, alpha }, { 0x0, 0x0, 0x0, 0xffff } }; int op = PictOpSrc, gop = PictOpSrc; int left, right; left = context->extents.left; right = context->extents.right; XOffsetRegion (region, x1, y1); XRenderSetPictureClipRegion (xdisplay, dst, region); XOffsetRegion (region, -x1, -y1); if (ignore_src_alpha) { XRenderComposite (xdisplay, PictOpSrc, src, None, dst, xSrc, ySrc, 0, 0, x1, y1, x2 - x1, y2 - y1); XRenderFillRectangle (xdisplay, PictOpAdd, dst, &color[2], x1, y1, x2 - x1, y2 - y1); gop = PictOpInReverse; } if (alpha != 0xffff) { op = PictOpIn; if (shade_alpha) { static XFixed stop[2] = { 0, 1 << 16 }; XTransform transform = { { { 1 << 16, 0, 0 }, { 0, 1 << 16, 0 }, { 0, 0, 1 << 16 } } }; Picture grad; XLinearGradient linear; XRadialGradient radial; XRenderPictureAttributes attrib; attrib.repeat = RepeatPad; radial.inner.x = 0; radial.inner.y = 0; radial.inner.radius = 0; radial.outer.x = 0; radial.outer.y = 0; /* left */ radial.outer.radius = left << 16; grad = XRenderCreateRadialGradient (xdisplay, &radial, stop, color, 2); transform.matrix[1][1] = (left << 16) / dy; transform.matrix[0][2] = -left << 16; if (direction < 0) transform.matrix[1][2] = -left << 16; XRenderSetPictureTransform (xdisplay, grad, &transform); XRenderChangePicture (xdisplay, grad, CPRepeat, &attrib); XRenderComposite (xdisplay, gop, grad, None, dst, 0, 0, 0, 0, x1, y1, left, dy); XRenderFreePicture (xdisplay, grad); /* middle */ linear.p1.x = 0; linear.p2.x = 0; if (direction > 0) { linear.p1.y = 0; linear.p2.y = dy << 16; } else { linear.p1.y = dy << 16; linear.p2.y = 0; } grad = XRenderCreateLinearGradient (xdisplay, &linear, stop, color, 2); XRenderChangePicture (xdisplay, grad, CPRepeat, &attrib); XRenderComposite (xdisplay, gop, grad, None, dst, 0, 0, 0, 0, x1 + left, y1, (x2 - x1) - left - right, dy); XRenderFreePicture (xdisplay, grad); /* right */ radial.outer.radius = right << 16; grad = XRenderCreateRadialGradient (xdisplay, &radial, stop, color, 2); transform.matrix[1][1] = (right << 16) / dy; transform.matrix[0][2] = 1 << 16; if (direction < 0) transform.matrix[1][2] = -right << 16; XRenderSetPictureTransform (xdisplay, grad, &transform); XRenderChangePicture (xdisplay, grad, CPRepeat, &attrib); XRenderComposite (xdisplay, gop, grad, None, dst, 0, 0, 0, 0, x2 - right, y1, right, dy); XRenderFreePicture (xdisplay, grad); } else { XRenderFillRectangle (xdisplay, gop, dst, &color[1], x1, y1, x2 - x1, y2 - y1); } } if (!ignore_src_alpha) XRenderComposite (xdisplay, op, src, None, dst, xSrc, ySrc, 0, 0, x1, y1, x2 - x1, y2 - y1); set_no_picture_clip (xdisplay, dst); } static void _decor_blend_vert_border_picture (Display *xdisplay, decor_context_t *context, Picture src, int xSrc, int ySrc, Picture dst, decor_layout_t *layout, Region region, unsigned short alpha, int shade_alpha, int x1, int y1, int x2, int y2, int direction, int ignore_src_alpha) { XRenderColor color[3] = { { 0xffff, 0xffff, 0xffff, 0xffff }, { alpha, alpha, alpha, alpha }, { 0x0, 0x0, 0x0, 0xffff } }; int op = PictOpSrc, gop = PictOpSrc; if (layout->rotation) { Region rotated_region; XRectangle rect; BoxPtr pBox = region->rects; int nBox = region->numRects; rotated_region = XCreateRegion (); while (nBox--) { rect.x = x1 + pBox->y1; rect.y = y1 + pBox->x1; rect.width = pBox->y2 - pBox->y1; rect.height = pBox->x2 - pBox->x1; XUnionRectWithRegion (&rect, rotated_region, rotated_region); ++pBox; } XRenderSetPictureClipRegion (xdisplay, dst, rotated_region); XDestroyRegion (rotated_region); } else { XOffsetRegion (region, x1, y1); XRenderSetPictureClipRegion (xdisplay, dst, region); XOffsetRegion (region, -x1, -y1); } if (ignore_src_alpha) { if (layout->rotation) { XTransform t = { { { 0, 1 << 16, 0 }, { 1 << 16, 0, 0 }, { 0, 0, 1 << 16 } } }; t.matrix[0][2] = xSrc << 16; t.matrix[1][2] = ySrc << 16; XRenderSetPictureTransform (xdisplay, src, &t); XRenderComposite (xdisplay, PictOpSrc, src, None, dst, 0, 0, 0, 0, x1, y1, x2 - x1, y2 - y1); XRenderFillRectangle (xdisplay, PictOpAdd, dst, &color[2], x1, y1, x2 - x1, y2 - y1); XRenderSetPictureTransform (xdisplay, src, &xident); } else { XRenderComposite (xdisplay, PictOpSrc, src, None, dst, xSrc, ySrc, 0, 0, x1, y1, x2 - x1, y2 - y1); XRenderFillRectangle (xdisplay, PictOpAdd, dst, &color[2], x1, y1, x2 - x1, y2 - y1); } gop = PictOpInReverse; } if (alpha != 0xffff) { op = PictOpIn; if (shade_alpha) { static XFixed stop[2] = { 0, 1 << 16 }; Picture grad; XLinearGradient linear; XRenderPictureAttributes attrib; attrib.repeat = RepeatPad; if (layout->rotation) { linear.p1.x = 0; linear.p2.x = 0; if (direction < 0) { linear.p1.y = 0; linear.p2.y = (y2 - y1) << 16; } else { linear.p1.y = (y2 - y1) << 16; linear.p2.y = 0 << 16; } } else { linear.p1.y = 0; linear.p2.y = 0; if (direction < 0) { linear.p1.x = 0; linear.p2.x = (x2 - x1) << 16; } else { linear.p1.x = (x2 - x1) << 16; linear.p2.x = 0; } } grad = XRenderCreateLinearGradient (xdisplay, &linear, stop, color, 2); XRenderChangePicture (xdisplay, grad, CPRepeat, &attrib); XRenderComposite (xdisplay, gop, grad, None, dst, 0, 0, 0, 0, x1, y1, x2 - x1, y2 - y1); XRenderFreePicture (xdisplay, grad); } else { XRenderFillRectangle (xdisplay, gop, dst, &color[1], x1, y1, x2 - x1, y2 - y1); } } if (!ignore_src_alpha) { if (layout->rotation) { XTransform t = { { { 0, 1 << 16, 0 }, { 1 << 16, 0, 0 }, { 0, 0, 1 << 16 } } }; t.matrix[0][2] = xSrc << 16; t.matrix[1][2] = ySrc << 16; XRenderSetPictureTransform (xdisplay, src, &t); XRenderComposite (xdisplay, op, src, None, dst, 0, 0, 0, 0, x1, y1, x2 - x1, y2 - y1); XRenderSetPictureTransform (xdisplay, src, &xident); } else { XRenderComposite (xdisplay, op, src, None, dst, xSrc, ySrc, 0, 0, x1, y1, x2 - x1, y2 - y1); } } set_no_picture_clip (xdisplay, dst); } void decor_blend_border_picture (Display *xdisplay, decor_context_t *context, Picture src, int xSrc, int ySrc, Picture dst, decor_layout_t *layout, unsigned int border, Region region, unsigned short alpha, int shade_alpha, int ignore_src_alpha) { int left, right, bottom, top; int x1, y1, x2, y2; left = context->extents.left; right = context->extents.right; top = context->extents.top; bottom = context->extents.bottom; switch (border) { case BORDER_TOP: x1 = layout->top.x1 + context->left_space - left; y1 = layout->top.y1 + context->top_space - top; x2 = layout->top.x2 - context->right_space + right; y2 = layout->top.y2; _decor_blend_horz_border_picture (xdisplay, context, src, xSrc, ySrc, dst, layout, region, alpha, shade_alpha, x1, y1, x2, y2, top, -1, ignore_src_alpha); _decor_pad_border_picture (xdisplay, dst, &layout->top); break; case BORDER_BOTTOM: x1 = layout->bottom.x1 + context->left_space - left; y1 = layout->bottom.y1; x2 = layout->bottom.x2 - context->right_space + right; y2 = layout->bottom.y1 + bottom; _decor_blend_horz_border_picture (xdisplay, context, src, xSrc, ySrc, dst, layout, region, alpha, shade_alpha, x1, y1, x2, y2, bottom, 1, ignore_src_alpha); _decor_pad_border_picture (xdisplay, dst, &layout->bottom); break; case BORDER_LEFT: x1 = layout->left.x1; y1 = layout->left.y1; x2 = layout->left.x2; y2 = layout->left.y2; if (layout->rotation) y1 += context->left_space - context->extents.left; else x1 += context->left_space - context->extents.left; _decor_blend_vert_border_picture (xdisplay, context, src, xSrc, ySrc, dst, layout, region, alpha, shade_alpha, x1, y1, x2, y2, 1, ignore_src_alpha); _decor_pad_border_picture (xdisplay, dst, &layout->left); break; case BORDER_RIGHT: x1 = layout->right.x1; y1 = layout->right.y1; x2 = layout->right.x2; y2 = layout->right.y2; if (layout->rotation) y2 -= context->right_space - context->extents.right; else x2 -= context->right_space - context->extents.right; _decor_blend_vert_border_picture (xdisplay, context, src, xSrc, ySrc, dst, layout, region, alpha, shade_alpha, x1, y1, x2, y2, -1, ignore_src_alpha); _decor_pad_border_picture (xdisplay, dst, &layout->right); break; default: break; } } int decor_post_pending (Display *xdisplay, Window client, unsigned int frame_type, unsigned int frame_state, unsigned int frame_actions) { XEvent event; Atom decor_pending = XInternAtom (xdisplay, DECOR_PIXMAP_PENDING_ATOM_NAME, FALSE); /* Send a client message indicating that a new * decoration can be generated for this window */ event.xclient.type = ClientMessage; event.xclient.window = client; event.xclient.message_type = decor_pending; event.xclient.format = 32; event.xclient.data.l[0] = frame_type; event.xclient.data.l[1] = frame_state; event.xclient.data.l[2] = frame_actions; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; XSendEvent (xdisplay, DefaultRootWindow (xdisplay), 0, StructureNotifyMask, &event); return 1; } int decor_post_generate_request (Display *xdisplay, Window client, unsigned int frame_type, unsigned int frame_state, unsigned int frame_actions) { XEvent event; Atom decor_request = XInternAtom (xdisplay, DECOR_REQUEST_PIXMAP_ATOM_NAME, FALSE); /* Send a client message indicating that a new * decoration can be generated for this window */ event.xclient.type = ClientMessage; event.xclient.window = client; event.xclient.message_type = decor_request; event.xclient.format = 32; event.xclient.data.l[0] = frame_type; event.xclient.data.l[1] = frame_state; event.xclient.data.l[2] = frame_actions; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; XSendEvent (xdisplay, DefaultRootWindow (xdisplay), 0, StructureNotifyMask, &event); return 1; } int decor_post_delete_pixmap (Display *xdisplay, Window window, Pixmap pixmap) { XEvent event; Atom decor_delete_pixmap = XInternAtom (xdisplay, DECOR_DELETE_PIXMAP_ATOM_NAME, FALSE); /* Send a client message indicating that this * pixmap is no longer in use and can be removed */ event.xclient.type = ClientMessage; event.xclient.window = window; event.xclient.message_type = decor_delete_pixmap; event.xclient.format = 32; event.xclient.data.l[0] = pixmap; event.xclient.data.l[1] = 0; event.xclient.data.l[2] = 0; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; XSendEvent (xdisplay, DefaultRootWindow (xdisplay), 0, StructureNotifyMask, &event); return 1; } int decor_acquire_dm_session (Display *xdisplay, int screen, const char *name, int replace_current_dm, Time *timestamp) { XEvent event; XSetWindowAttributes attr; Window current_dm_sn_owner, new_dm_sn_owner; Atom dm_sn_atom; Atom manager_atom; Atom dm_name_atom; Atom utf8_string_atom; Time dm_sn_timestamp; char buf[128]; manager_atom = XInternAtom (xdisplay, "MANAGER", FALSE); dm_name_atom = XInternAtom (xdisplay, "_COMPIZ_DM_NAME", 0); utf8_string_atom = XInternAtom (xdisplay, "UTF8_STRING", 0); snprintf (buf, 128, "_COMPIZ_DM_S%d", screen); dm_sn_atom = XInternAtom (xdisplay, buf, 0); current_dm_sn_owner = XGetSelectionOwner (xdisplay, dm_sn_atom); if (current_dm_sn_owner != None) { if (!replace_current_dm) return DECOR_ACQUIRE_STATUS_OTHER_DM_RUNNING; XSelectInput (xdisplay, current_dm_sn_owner, StructureNotifyMask); } attr.override_redirect = TRUE; attr.event_mask = PropertyChangeMask; new_dm_sn_owner = XCreateWindow (xdisplay, XRootWindow (xdisplay, screen), -100, -100, 1, 1, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWEventMask, &attr); XChangeProperty (xdisplay, new_dm_sn_owner, dm_name_atom, utf8_string_atom, 8, PropModeReplace, (unsigned char *) name, strlen (name)); XWindowEvent (xdisplay, new_dm_sn_owner, PropertyChangeMask, &event); dm_sn_timestamp = event.xproperty.time; XSetSelectionOwner (xdisplay, dm_sn_atom, new_dm_sn_owner, dm_sn_timestamp); if (XGetSelectionOwner (xdisplay, dm_sn_atom) != new_dm_sn_owner) { XDestroyWindow (xdisplay, new_dm_sn_owner); return DECOR_ACQUIRE_STATUS_FAILED; } /* Send client message indicating that we are now the DM */ event.xclient.type = ClientMessage; event.xclient.window = XRootWindow (xdisplay, screen); event.xclient.message_type = manager_atom; event.xclient.format = 32; event.xclient.data.l[0] = dm_sn_timestamp; event.xclient.data.l[1] = dm_sn_atom; event.xclient.data.l[2] = 0; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; XSendEvent (xdisplay, XRootWindow (xdisplay, screen), 0, StructureNotifyMask, &event); /* Wait for old decoration manager to go away */ if (current_dm_sn_owner != None) { do { XWindowEvent (xdisplay, current_dm_sn_owner, StructureNotifyMask, &event); } while (event.type != DestroyNotify); } *timestamp = dm_sn_timestamp; return DECOR_ACQUIRE_STATUS_SUCCESS; } void decor_set_dm_check_hint (Display *xdisplay, int screen, int supports) { XSetWindowAttributes attrs; unsigned long data; Window xroot; Atom atom; Atom type_pixmap_atom; Atom type_window_atom; Atom type_supported_atom; Atom supported_deco_atoms[2]; int i; type_supported_atom = XInternAtom (xdisplay, DECOR_TYPE_ATOM_NAME, 0); type_pixmap_atom = XInternAtom (xdisplay, DECOR_TYPE_PIXMAP_ATOM_NAME, 0); type_window_atom = XInternAtom (xdisplay, DECOR_TYPE_WINDOW_ATOM_NAME, 0); if (!supports) return; attrs.override_redirect = 1; attrs.event_mask = PropertyChangeMask; xroot = RootWindow (xdisplay, screen); data = XCreateWindow (xdisplay, xroot, -100, -100, 1, 1, 0, CopyFromParent, CopyFromParent, (Visual *) CopyFromParent, CWOverrideRedirect | CWEventMask, &attrs); i = 0; if (supports & WINDOW_DECORATION_TYPE_PIXMAP) { supported_deco_atoms[i] = type_pixmap_atom; ++i; } if (supports & WINDOW_DECORATION_TYPE_WINDOW) { supported_deco_atoms[i] = type_window_atom; ++i; } XChangeProperty (xdisplay, data, type_supported_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *) supported_deco_atoms, i); atom = XInternAtom (xdisplay, DECOR_SUPPORTING_DM_CHECK_ATOM_NAME, 0); XChangeProperty (xdisplay, xroot, atom, XA_WINDOW, 32, PropModeReplace, (unsigned char *) &data, 1); } /* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ static int convert_property (Display *xdisplay, Window w, Atom target, Atom property, Time dm_sn_timestamp) { static const unsigned short N_TARGETS = 4; Atom conversion_targets[N_TARGETS]; conversion_targets[0] = XInternAtom (xdisplay, "TARGETS", 0); conversion_targets[1] = XInternAtom (xdisplay, "MULTIPLE", 0); conversion_targets[2] = XInternAtom (xdisplay, "TIMESTAMP", 0); conversion_targets[3] = XInternAtom (xdisplay, "VERSION", 0); if (target == conversion_targets[0]) XChangeProperty (xdisplay, w, property, XA_ATOM, 32, PropModeReplace, (unsigned char *) conversion_targets, N_TARGETS); else if (target == conversion_targets[2]) XChangeProperty (xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *) &dm_sn_timestamp, 1); else if (target == conversion_targets[3]) { long icccm_version[] = { 2, 0 }; XChangeProperty (xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *) icccm_version, 2); } else return 0; /* Be sure the PropertyNotify has arrived so we * can send SelectionNotify */ XSync (xdisplay, 0); return 1; } void decor_handle_selection_request (Display *xdisplay, XEvent *event, Time timestamp) { XSelectionEvent reply; Atom multiple_atom; Atom atom_pair_atom; reply.type = SelectionNotify; reply.display = xdisplay; reply.requestor = event->xselectionrequest.requestor; reply.selection = event->xselectionrequest.selection; reply.target = event->xselectionrequest.target; reply.property = None; reply.time = event->xselectionrequest.time; multiple_atom = XInternAtom (xdisplay, "MULTIPLE", 0); atom_pair_atom = XInternAtom (xdisplay, "ATOM_PAIR", 0); if (event->xselectionrequest.target == multiple_atom) { if (event->xselectionrequest.property != None) { Atom type, *adata; int i, format; unsigned long num, rest; unsigned char *data; if (XGetWindowProperty (xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, 0, 256, FALSE, atom_pair_atom, &type, &format, &num, &rest, &data) != Success) return; /* FIXME: to be 100% correct, should deal with rest > 0, * but since we have 4 possible targets, we will hardly ever * meet multiple requests with a length > 8 */ adata = (Atom *) data; i = 0; while (i < (int) num) { if (!convert_property (xdisplay, event->xselectionrequest.requestor, adata[i], adata[i + 1], timestamp)) adata[i + 1] = None; i += 2; } XChangeProperty (xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, atom_pair_atom, 32, PropModeReplace, data, num); } } else { if (event->xselectionrequest.property == None) event->xselectionrequest.property = event->xselectionrequest.target; if (convert_property (xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.target, event->xselectionrequest.property, timestamp)) reply.property = event->xselectionrequest.property; } XSendEvent (xdisplay, event->xselectionrequest.requestor, FALSE, 0L, (XEvent *) &reply); } int decor_handle_selection_clear (Display *xdisplay, XEvent *xevent, int screen) { Atom dm_sn_atom; char buf[128]; snprintf (buf, 128, "_COMPIZ_DM_S%d", screen); dm_sn_atom = XInternAtom (xdisplay, buf, 0); if (xevent->xselectionclear.selection == dm_sn_atom) return DECOR_SELECTION_GIVE_UP; return DECOR_SELECTION_KEEP; }