Skip to content
decoration.c 72.7 KiB
Newer Older
/*
 * 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 <davidr@novell.com>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <X11/Xatom.h>
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
  --------------------------------------
  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)
{
    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;
}

			 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,
Sam Spilsbury's avatar
Sam Spilsbury committed
			 unsigned int	 frame_type,
			 unsigned int    frame_state,
    /* 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);
    *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;
Sam Spilsbury's avatar
Sam Spilsbury committed
    *data++ = frame_state;
    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;

void
decor_gen_window_property (long		   *data,
			   decor_extents_t *input,
			   decor_extents_t *max_input,
			   int		   min_width,
Sam Spilsbury's avatar
Sam Spilsbury committed
			   unsigned int	   frame_type,
			   unsigned int    frame_state,
    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;
Sam Spilsbury's avatar
Sam Spilsbury committed
    *data++ = frame_state;
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,
				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,
Sam Spilsbury's avatar
Sam Spilsbury committed
				unsigned int     *frame_state,
				unsigned int     *frame_actions,
				decor_quad_t     *quad)
    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 ())
    if (decor_property_get_type (data) != WINDOW_DECORATION_TYPE_PIXMAP)
    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++;

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)
           );
}

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;
	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]))
int
decor_window_property (long	       *data,
		       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 ())

    if (decor_property_get_type (data) != WINDOW_DECORATION_TYPE_WINDOW)
    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++;

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;

    }

    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,
{
    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;
    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;

    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;

    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;
    }
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,

    q->p1.x	  = -left;
    q->p1.y	  = top;
    q->p1.gravity = gravity | GRAVITY_WEST;
    q->p2.gravity = gravity | splitGravity;
    q->max_width  = left + left_corner;
    q->max_height = SHRT_MAX;
    q->align	  = ALIGN_LEFT;
    q->clamp	  = 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->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.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->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;

decor_set_lSrS_window_quads (decor_quad_t    *q,
			     decor_context_t *c,
			     decor_layout_t  *l)
    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,

    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,
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,
    n = decor_set_lSrS_window_quads (q, c, l);