1
0
Fork 0
mutter-performance-source/src/theme.c
Havoc Pennington b52ee424e4 Only consider the bottom of the titlebar a resize control; I keep
2002-01-27  Havoc Pennington  <hp@pobox.com>

	* src/frames.c (get_control): Only consider the bottom of the
	titlebar a resize control; I keep accidentally resizing windows
	instead of activating them. Also, give south resizing priority
	over north, if the window is so small the active regions overlap

	* src/theme.c: add MetaTheme, get MetaFrameStyleSet into
	a usable state

	* src/common.h: move window type back to window.h, decided
	not to use it on frame side
	(MetaFrameType): add this instead
2002-01-27 08:21:53 +00:00

1303 lines
37 KiB
C

/* Metacity Theme Rendering */
/*
* Copyright (C) 2001 Havoc Pennington
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "theme.h"
#include "util.h"
#include "gradient.h"
#include <gtk/gtkwidget.h>
#include <string.h>
#define GDK_COLOR_RGBA(color) \
(0xff | \
(((color).red / 256) << 24) | \
(((color).green / 256) << 16) | \
(((color).blue / 256) << 8))
#define GDK_COLOR_RGB(color) \
((((color).red / 256) << 16) | \
(((color).green / 256) << 8) | \
(((color).blue / 256)))
static void
color_composite (const GdkColor *bg,
const GdkColor *fg,
double alpha_d,
GdkColor *color)
{
guint16 alpha;
*color = *bg;
alpha = alpha_d * 0xffff;
color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
color->green = color->green + (((fg->green - color->green) * alpha + 0x8000) >> 16);
color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
}
MetaFrameLayout*
meta_frame_layout_new (void)
{
MetaFrameLayout *layout;
layout = g_new0 (MetaFrameLayout, 1);
return layout;
}
void
meta_frame_layout_free (MetaFrameLayout *layout)
{
g_return_if_fail (layout != NULL);
g_free (layout);
}
void
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int *top_height,
int *bottom_height,
int *left_width,
int *right_width)
{
int buttons_height, title_height, spacer_height;
g_return_if_fail (top_height != NULL);
g_return_if_fail (bottom_height != NULL);
g_return_if_fail (left_width != NULL);
g_return_if_fail (right_width != NULL);
buttons_height = layout->button_height +
layout->button_border.top + layout->button_border.bottom;
title_height = text_height +
layout->text_border.top + layout->text_border.bottom +
layout->title_border.top + layout->title_border.bottom;
spacer_height = layout->spacer_height;
if (top_height)
{
*top_height = MAX (buttons_height, title_height);
*top_height = MAX (*top_height, spacer_height);
}
if (left_width)
*left_width = layout->left_width;
if (right_width)
*right_width = layout->right_width;
if (bottom_height)
{
if (flags & META_FRAME_SHADED)
*bottom_height = 0;
else
*bottom_height = layout->bottom_height;
}
}
void
meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
GtkWidget *widget,
int text_height,
MetaFrameFlags flags,
int client_width,
int client_height,
MetaFrameGeometry *fgeom)
{
int x;
int button_y;
int title_right_edge;
int width, height;
meta_frame_layout_get_borders (layout, widget, text_height,
flags,
&fgeom->top_height,
&fgeom->bottom_height,
&fgeom->left_width,
&fgeom->right_width);
width = client_width + fgeom->left_width + fgeom->right_width;
height = client_height + fgeom->top_height + fgeom->bottom_height;
fgeom->width = width;
fgeom->height = height;
x = width - layout->right_inset;
/* center buttons */
button_y = (fgeom->top_height -
(layout->button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top;
if ((flags & META_FRAME_ALLOWS_DELETE) &&
x >= 0)
{
fgeom->close_rect.x = x - layout->button_border.right - layout->button_width;
fgeom->close_rect.y = button_y;
fgeom->close_rect.width = layout->button_width;
fgeom->close_rect.height = layout->button_height;
x = fgeom->close_rect.x - layout->button_border.left;
}
else
{
fgeom->close_rect.x = 0;
fgeom->close_rect.y = 0;
fgeom->close_rect.width = 0;
fgeom->close_rect.height = 0;
}
if ((flags & META_FRAME_ALLOWS_MAXIMIZE) &&
x >= 0)
{
fgeom->max_rect.x = x - layout->button_border.right - layout->button_width;
fgeom->max_rect.y = button_y;
fgeom->max_rect.width = layout->button_width;
fgeom->max_rect.height = layout->button_height;
x = fgeom->max_rect.x - layout->button_border.left;
}
else
{
fgeom->max_rect.x = 0;
fgeom->max_rect.y = 0;
fgeom->max_rect.width = 0;
fgeom->max_rect.height = 0;
}
if ((flags & META_FRAME_ALLOWS_MINIMIZE) &&
x >= 0)
{
fgeom->min_rect.x = x - layout->button_border.right - layout->button_width;
fgeom->min_rect.y = button_y;
fgeom->min_rect.width = layout->button_width;
fgeom->min_rect.height = layout->button_height;
x = fgeom->min_rect.x - layout->button_border.left;
}
else
{
fgeom->min_rect.x = 0;
fgeom->min_rect.y = 0;
fgeom->min_rect.width = 0;
fgeom->min_rect.height = 0;
}
if ((fgeom->close_rect.width > 0 ||
fgeom->max_rect.width > 0 ||
fgeom->min_rect.width > 0) &&
x >= 0)
{
fgeom->spacer_rect.x = x - layout->spacer_padding - layout->spacer_width;
fgeom->spacer_rect.y = (fgeom->top_height - layout->spacer_height) / 2;
fgeom->spacer_rect.width = layout->spacer_width;
fgeom->spacer_rect.height = layout->spacer_height;
x = fgeom->spacer_rect.x - layout->spacer_padding;
}
else
{
fgeom->spacer_rect.x = 0;
fgeom->spacer_rect.y = 0;
fgeom->spacer_rect.width = 0;
fgeom->spacer_rect.height = 0;
}
title_right_edge = x - layout->title_border.right;
/* Now x changes to be position from the left */
x = layout->left_inset;
if (flags & META_FRAME_ALLOWS_MENU)
{
fgeom->menu_rect.x = x + layout->button_border.left;
fgeom->menu_rect.y = button_y;
fgeom->menu_rect.width = layout->button_width;
fgeom->menu_rect.height = layout->button_height;
x = fgeom->menu_rect.x + fgeom->menu_rect.width + layout->button_border.right;
}
else
{
fgeom->menu_rect.x = 0;
fgeom->menu_rect.y = 0;
fgeom->menu_rect.width = 0;
fgeom->menu_rect.height = 0;
}
/* If menu overlaps close button, then the menu wins since it
* lets you perform any operation including close
*/
if (fgeom->close_rect.width > 0 &&
fgeom->close_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
{
fgeom->close_rect.width = 0;
fgeom->close_rect.height = 0;
}
/* Check for maximize overlap */
if (fgeom->max_rect.width > 0 &&
fgeom->max_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
{
fgeom->max_rect.width = 0;
fgeom->max_rect.height = 0;
}
/* Check for minimize overlap */
if (fgeom->min_rect.width > 0 &&
fgeom->min_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
{
fgeom->min_rect.width = 0;
fgeom->min_rect.height = 0;
}
/* Check for spacer overlap */
if (fgeom->spacer_rect.width > 0 &&
fgeom->spacer_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height))
{
fgeom->spacer_rect.width = 0;
fgeom->spacer_rect.height = 0;
}
/* We always fill as much vertical space as possible with title rect,
* rather than centering it like the buttons and spacer
*/
fgeom->title_rect.x = x + layout->title_border.left;
fgeom->title_rect.y = layout->title_border.top;
fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom;
/* Nuke title if it won't fit */
if (fgeom->title_rect.width < 0 ||
fgeom->title_rect.height < 0)
{
fgeom->title_rect.width = 0;
fgeom->title_rect.height = 0;
}
}
MetaGradientSpec*
meta_gradient_spec_new (MetaGradientType type)
{
MetaGradientSpec *spec;
spec = g_new (MetaGradientSpec, 1);
spec->type = type;
spec->color_specs = NULL;
return spec;
}
void
meta_gradient_spec_free (MetaGradientSpec *spec)
{
g_return_if_fail (spec != NULL);
g_slist_foreach (spec->color_specs, (GFunc) meta_color_spec_free, NULL);
g_slist_free (spec->color_specs);
g_free (spec);
}
GdkPixbuf*
meta_gradient_spec_render (const MetaGradientSpec *spec,
GtkWidget *widget,
int width,
int height)
{
int n_colors;
GdkColor *colors;
GSList *tmp;
int i;
GdkPixbuf *pixbuf;
n_colors = g_slist_length (spec->color_specs);
if (n_colors == 0)
return NULL;
colors = g_new (GdkColor, n_colors);
i = 0;
tmp = spec->color_specs;
while (tmp != NULL)
{
meta_color_spec_render (tmp->data, widget, &colors[i]);
tmp = tmp->next;
++i;
}
pixbuf = meta_gradient_create_multi (width, height,
colors, n_colors,
spec->type);
g_free (colors);
return pixbuf;
}
MetaColorSpec*
meta_color_spec_new (MetaColorSpecType type)
{
MetaColorSpec *spec;
MetaColorSpec dummy;
int size;
size = G_STRUCT_OFFSET (MetaColorSpec, data);
switch (type)
{
case META_COLOR_SPEC_BASIC:
size += sizeof (dummy.data.basic);
break;
case META_COLOR_SPEC_GTK:
size += sizeof (dummy.data.gtk);
break;
case META_COLOR_SPEC_BLEND:
size += sizeof (dummy.data.blend);
break;
}
spec = g_malloc0 (size);
spec->type = type;
return spec;
}
void
meta_color_spec_free (MetaColorSpec *spec)
{
g_return_if_fail (spec != NULL);
switch (spec->type)
{
case META_COLOR_SPEC_BASIC:
break;
case META_COLOR_SPEC_GTK:
break;
case META_COLOR_SPEC_BLEND:
if (spec->data.blend.foreground)
meta_color_spec_free (spec->data.blend.foreground);
if (spec->data.blend.background)
meta_color_spec_free (spec->data.blend.background);
break;
}
g_free (spec);
}
void
meta_color_spec_render (MetaColorSpec *spec,
GtkWidget *widget,
GdkColor *color)
{
g_return_if_fail (spec != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (widget->style != NULL);
switch (spec->type)
{
case META_COLOR_SPEC_BASIC:
*color = spec->data.basic.color;
break;
case META_COLOR_SPEC_GTK:
switch (spec->data.gtk.component)
{
case GTK_RC_BG:
*color = widget->style->bg[spec->data.gtk.state];
break;
case GTK_RC_FG:
*color = widget->style->fg[spec->data.gtk.state];
break;
case GTK_RC_BASE:
*color = widget->style->base[spec->data.gtk.state];
break;
case GTK_RC_TEXT:
*color = widget->style->text[spec->data.gtk.state];
break;
}
break;
case META_COLOR_SPEC_BLEND:
{
GdkColor bg, fg;
meta_color_spec_render (spec->data.blend.background, widget, &bg);
meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
color_composite (&bg, &fg, spec->data.blend.alpha, color);
}
break;
}
}
MetaTextureSpec*
meta_texture_spec_new (MetaTextureType type)
{
MetaTextureSpec *spec;
MetaTextureSpec dummy;
int size;
size = G_STRUCT_OFFSET (MetaTextureSpec, data);
switch (type)
{
case META_TEXTURE_SOLID:
size += sizeof (dummy.data.solid);
break;
case META_TEXTURE_GRADIENT:
size += sizeof (dummy.data.gradient);
break;
case META_TEXTURE_IMAGE:
size += sizeof (dummy.data.image);
break;
case META_TEXTURE_COMPOSITE:
size += sizeof (dummy.data.composite);
break;
}
spec = g_malloc0 (size);
spec->type = type;
return spec;
}
void
meta_texture_spec_free (MetaTextureSpec *spec)
{
g_return_if_fail (spec != NULL);
switch (spec->type)
{
case META_TEXTURE_SOLID:
if (spec->data.solid.color_spec)
meta_color_spec_free (spec->data.solid.color_spec);
break;
case META_TEXTURE_GRADIENT:
if (spec->data.gradient.gradient_spec)
meta_gradient_spec_free (spec->data.gradient.gradient_spec);
break;
case META_TEXTURE_IMAGE:
if (spec->data.image.pixbuf)
g_object_unref (G_OBJECT (spec->data.image.pixbuf));
break;
case META_TEXTURE_COMPOSITE:
if (spec->data.composite.background)
meta_texture_spec_free (spec->data.composite.background);
if (spec->data.composite.foreground)
meta_texture_spec_free (spec->data.composite.foreground);
}
g_free (spec);
}
static void
render_pixbuf (GdkDrawable *drawable,
const GdkRectangle *clip,
GdkPixbuf *pixbuf,
int x,
int y)
{
/* grumble, render_to_drawable_alpha does not accept a clip
* mask, so we have to go through some BS
*/
GdkRectangle pixbuf_rect;
GdkRectangle draw_rect;
pixbuf_rect.x = x;
pixbuf_rect.y = y;
pixbuf_rect.width = gdk_pixbuf_get_width (pixbuf);
pixbuf_rect.height = gdk_pixbuf_get_height (pixbuf);
if (clip)
{
if (!gdk_rectangle_intersect ((GdkRectangle*)clip,
&pixbuf_rect, &draw_rect))
return;
}
else
{
draw_rect = pixbuf_rect;
}
gdk_pixbuf_render_to_drawable_alpha (pixbuf,
drawable,
draw_rect.x - pixbuf_rect.x,
draw_rect.y - pixbuf_rect.y,
draw_rect.x, draw_rect.y,
draw_rect.width,
draw_rect.height,
GDK_PIXBUF_ALPHA_FULL, /* ignored */
128, /* ignored */
GDK_RGB_DITHER_NORMAL,
draw_rect.x - pixbuf_rect.x,
draw_rect.y - pixbuf_rect.y);
}
static void
render_pixbuf_aligned (GdkDrawable *drawable,
const GdkRectangle *clip,
GdkPixbuf *pixbuf,
double xalign,
double yalign,
int x,
int y,
int width,
int height)
{
int pix_width;
int pix_height;
int rx, ry;
pix_width = gdk_pixbuf_get_width (pixbuf);
pix_height = gdk_pixbuf_get_height (pixbuf);
rx = x + (width - pix_width) * xalign;
ry = y + (height - pix_height) * yalign;
render_pixbuf (drawable, clip, pixbuf, rx, ry);
}
static GdkPixbuf*
multiply_alpha (GdkPixbuf *pixbuf,
guchar alpha)
{
GdkPixbuf *new_pixbuf;
guchar *pixels;
int rowstride;
int height;
int row;
if (alpha == 255)
return pixbuf;
if (!gdk_pixbuf_get_has_alpha (pixbuf))
{
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
row = 0;
while (row < height)
{
guchar *p;
guchar *end;
p = pixels + row * rowstride;
end = p + rowstride;
while (p != end)
{
p += 3; /* skip RGB */
/* multiply the two alpha channels. not sure this is right.
* but some end cases are that if the pixbuf contains 255,
* then it should be modified to contain "alpha"; if the
* pixbuf contains 0, it should remain 0.
*/
*p = (*p * alpha) / 65025; /* (*p / 255) * (alpha / 255); */
++p; /* skip A */
}
++row;
}
return pixbuf;
}
static GdkPixbuf*
meta_texture_spec_render (const MetaTextureSpec *spec,
GtkWidget *widget,
MetaTextureDrawMode mode,
guchar alpha,
int width,
int height)
{
GdkPixbuf *pixbuf;
pixbuf = NULL;
g_return_val_if_fail (spec != NULL, NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (widget->style != NULL, NULL);
switch (spec->type)
{
case META_TEXTURE_SOLID:
{
GdkColor color;
g_return_val_if_fail (spec->data.solid.color_spec != NULL,
NULL);
meta_color_spec_render (spec->data.solid.color_spec,
widget, &color);
if (alpha == 255)
{
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
FALSE,
8, width, height);
gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
}
else
{
guint32 rgba;
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
TRUE,
8, width, height);
rgba = GDK_COLOR_RGBA (color);
rgba &= ~0xff;
rgba |= alpha;
gdk_pixbuf_fill (pixbuf, rgba);
}
}
break;
case META_TEXTURE_GRADIENT:
{
GdkPixbuf *pixbuf;
g_return_val_if_fail (spec->data.gradient.gradient_spec != NULL,
NULL);
pixbuf = meta_gradient_spec_render (spec->data.gradient.gradient_spec,
widget, width, height);
pixbuf = multiply_alpha (pixbuf, alpha);
}
break;
case META_TEXTURE_IMAGE:
{
GdkPixbuf *pixbuf;
g_return_val_if_fail (spec->data.image.pixbuf != NULL,
NULL);
pixbuf = NULL;
switch (mode)
{
case META_TEXTURE_DRAW_UNSCALED:
pixbuf = spec->data.image.pixbuf;
g_object_ref (G_OBJECT (pixbuf));
break;
case META_TEXTURE_DRAW_SCALED_VERTICALLY:
pixbuf = spec->data.image.pixbuf;
if (gdk_pixbuf_get_height (pixbuf) == height)
{
g_object_ref (G_OBJECT (pixbuf));
}
else
{
pixbuf = gdk_pixbuf_scale_simple (pixbuf,
gdk_pixbuf_get_width (pixbuf),
height,
GDK_INTERP_BILINEAR);
}
break;
case META_TEXTURE_DRAW_SCALED_HORIZONTALLY:
pixbuf = spec->data.image.pixbuf;
if (gdk_pixbuf_get_width (pixbuf) == width)
{
g_object_ref (G_OBJECT (pixbuf));
}
else
{
pixbuf = gdk_pixbuf_scale_simple (pixbuf,
width,
gdk_pixbuf_get_height (pixbuf),
GDK_INTERP_BILINEAR);
}
break;
case META_TEXTURE_DRAW_SCALED_BOTH:
pixbuf = spec->data.image.pixbuf;
if (gdk_pixbuf_get_width (pixbuf) == width &&
gdk_pixbuf_get_height (pixbuf) == height)
{
g_object_ref (G_OBJECT (pixbuf));
}
else
{
pixbuf = gdk_pixbuf_scale_simple (pixbuf,
width, height,
GDK_INTERP_BILINEAR);
}
break;
}
pixbuf = multiply_alpha (pixbuf, alpha);
}
break;
case META_TEXTURE_COMPOSITE:
break;
}
return pixbuf;
}
static void
draw_color_rectangle (GtkWidget *widget,
GdkDrawable *drawable,
GdkColor *color,
const GdkRectangle *clip,
int x,
int y,
int width,
int height)
{
GdkGC *gc;
GdkGCValues values;
values.foreground = *color;
gdk_rgb_find_color (widget->style->colormap, &values.foreground);
gc = gdk_gc_new_with_values (drawable, &values, GDK_GC_FOREGROUND);
if (clip)
gdk_gc_set_clip_rectangle (gc,
(GdkRectangle*) clip); /* const cast */
gdk_draw_rectangle (drawable,
gc, TRUE, x, y, width, height);
g_object_unref (G_OBJECT (gc));
}
static void
draw_bg_solid_composite (const MetaTextureSpec *bg,
const MetaTextureSpec *fg,
double alpha,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
MetaTextureDrawMode mode,
double xalign,
double yalign,
int x,
int y,
int width,
int height)
{
GdkColor bg_color;
g_assert (bg->type == META_TEXTURE_SOLID);
g_assert (fg->type != META_TEXTURE_COMPOSITE);
meta_color_spec_render (bg->data.solid.color_spec,
widget,
&bg_color);
switch (fg->type)
{
case META_TEXTURE_SOLID:
{
GdkColor fg_color;
meta_color_spec_render (fg->data.solid.color_spec,
widget,
&fg_color);
color_composite (&bg_color, &fg_color,
alpha, &fg_color);
draw_color_rectangle (widget, drawable, &fg_color, clip,
x, y, width, height);
}
break;
case META_TEXTURE_GRADIENT:
/* FIXME I think we could just composite all the colors in
* the gradient prior to generating the gradient?
*/
/* FALL THRU */
case META_TEXTURE_IMAGE:
{
GdkPixbuf *pixbuf;
GdkPixbuf *composited;
pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
width, height);
if (pixbuf == NULL)
return;
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
gdk_pixbuf_get_has_alpha (pixbuf), 8,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf));
if (composited == NULL)
{
g_object_unref (G_OBJECT (pixbuf));
return;
}
gdk_pixbuf_composite_color (pixbuf,
composited,
0, 0,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
0.0, 0.0, /* offsets */
1.0, 1.0, /* scale */
GDK_INTERP_BILINEAR,
255 * alpha,
0, 0, /* check offsets */
0, /* check size */
GDK_COLOR_RGB (bg_color),
GDK_COLOR_RGB (bg_color));
/* Need to draw background since pixbuf is not
* necessarily covering the whole thing
*/
draw_color_rectangle (widget, drawable, &bg_color, clip,
x, y, width, height);
render_pixbuf_aligned (drawable, clip, composited,
xalign, yalign,
x, y, width, height);
g_object_unref (G_OBJECT (pixbuf));
g_object_unref (G_OBJECT (composited));
}
break;
case META_TEXTURE_COMPOSITE:
g_assert_not_reached ();
break;
}
}
static void
draw_bg_gradient_composite (const MetaTextureSpec *bg,
const MetaTextureSpec *fg,
double alpha,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
MetaTextureDrawMode mode,
double xalign,
double yalign,
int x,
int y,
int width,
int height)
{
g_assert (bg->type == META_TEXTURE_GRADIENT);
g_assert (fg->type != META_TEXTURE_COMPOSITE);
switch (fg->type)
{
case META_TEXTURE_SOLID:
case META_TEXTURE_GRADIENT:
case META_TEXTURE_IMAGE:
{
GdkPixbuf *bg_pixbuf;
GdkPixbuf *fg_pixbuf;
GdkPixbuf *composited;
int fg_width, fg_height;
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
width, height);
if (bg_pixbuf == NULL)
return;
fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
width, height);
if (fg_pixbuf == NULL)
{
g_object_unref (G_OBJECT (bg_pixbuf));
return;
}
/* gradients always fill the entire target area */
g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
gdk_pixbuf_get_width (bg_pixbuf),
gdk_pixbuf_get_height (bg_pixbuf));
if (composited == NULL)
{
g_object_unref (G_OBJECT (bg_pixbuf));
g_object_unref (G_OBJECT (fg_pixbuf));
return;
}
fg_width = gdk_pixbuf_get_width (fg_pixbuf);
fg_height = gdk_pixbuf_get_height (fg_pixbuf);
/* If we wanted to be all cool we could deal with the
* offsets and try to composite only in the clip rectangle,
* but I just don't care enough to figure it out.
*/
gdk_pixbuf_composite (fg_pixbuf,
composited,
x + (width - fg_width) * xalign,
y + (height - fg_height) * yalign,
gdk_pixbuf_get_width (fg_pixbuf),
gdk_pixbuf_get_height (fg_pixbuf),
0.0, 0.0, /* offsets */
1.0, 1.0, /* scale */
GDK_INTERP_BILINEAR,
255 * alpha);
render_pixbuf (drawable, clip, composited, x, y);
g_object_unref (G_OBJECT (bg_pixbuf));
g_object_unref (G_OBJECT (fg_pixbuf));
g_object_unref (G_OBJECT (composited));
}
break;
case META_TEXTURE_COMPOSITE:
g_assert_not_reached ();
break;
}
}
static void
draw_bg_image_composite (const MetaTextureSpec *bg,
const MetaTextureSpec *fg,
double alpha,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
MetaTextureDrawMode mode,
double xalign,
double yalign,
int x,
int y,
int width,
int height)
{
g_assert (bg->type == META_TEXTURE_IMAGE);
g_assert (fg->type != META_TEXTURE_COMPOSITE);
/* This one is tricky since the image doesn't cover the entire x,y
* width, height rectangle, so we need to handle the fact that there
* may be existing stuff in the uncovered portions of the drawable
* that we need to composite over the top of.
*
* i.e. the "bg" we are compositing onto is equivalent to the image
* composited over the top of whatever is already in the drawable.
*
* To implement this we just draw the background to drawable, then
* render the foreground to a pixbuf, multiply its alpha channel by
* the composite alpha, then composite the foreground onto the
* drawable.
*/
switch (fg->type)
{
case META_TEXTURE_SOLID:
case META_TEXTURE_GRADIENT:
case META_TEXTURE_IMAGE:
{
GdkPixbuf *bg_pixbuf, *fg_pixbuf;
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
width, height);
if (bg_pixbuf == NULL)
return;
/* fg_pixbuf has its alpha multiplied, note */
fg_pixbuf = meta_texture_spec_render (fg, widget, mode,
255 * alpha,
width, height);
if (fg_pixbuf == NULL)
{
g_object_unref (G_OBJECT (bg_pixbuf));
return;
}
render_pixbuf_aligned (drawable, clip, bg_pixbuf,
xalign, yalign,
x, y, width, height);
render_pixbuf_aligned (drawable, clip, fg_pixbuf,
xalign, yalign,
x, y, width, height);
g_object_unref (G_OBJECT (bg_pixbuf));
g_object_unref (G_OBJECT (fg_pixbuf));
}
break;
case META_TEXTURE_COMPOSITE:
g_assert_not_reached ();
break;
}
}
void
meta_texture_spec_draw (const MetaTextureSpec *spec,
GtkWidget *widget,
GdkDrawable *drawable,
const GdkRectangle *clip,
MetaTextureDrawMode mode,
double xalign,
double yalign,
int x,
int y,
int width,
int height)
{
g_return_if_fail (spec != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GDK_IS_DRAWABLE (drawable));
g_return_if_fail (widget->style != NULL);
switch (spec->type)
{
case META_TEXTURE_SOLID:
{
GdkColor color;
g_return_if_fail (spec->data.solid.color_spec != NULL);
meta_color_spec_render (spec->data.solid.color_spec,
widget, &color);
draw_color_rectangle (widget, drawable, &color, clip,
x, y, width, height);
}
break;
case META_TEXTURE_GRADIENT:
case META_TEXTURE_IMAGE:
{
GdkPixbuf *pixbuf;
g_return_if_fail (spec->data.gradient.gradient_spec != NULL);
pixbuf = meta_texture_spec_render (spec, widget, mode, 255,
width, height);
if (pixbuf == NULL)
return;
render_pixbuf_aligned (drawable, clip, pixbuf,
xalign, yalign,
x, y, width, height);
g_object_unref (G_OBJECT (pixbuf));
}
break;
case META_TEXTURE_COMPOSITE:
{
MetaTextureSpec *fg;
MetaTextureSpec *bg;
/* We could just render both things to a pixbuf then squish them
* but we are instead going to try to be all optimized for
* certain cases.
*/
fg = spec->data.composite.foreground;
bg = spec->data.composite.background;
g_return_if_fail (fg != NULL);
g_return_if_fail (bg != NULL);
g_return_if_fail (fg->type != META_TEXTURE_COMPOSITE);
g_return_if_fail (bg->type != META_TEXTURE_COMPOSITE);
switch (bg->type)
{
case META_TEXTURE_SOLID:
draw_bg_solid_composite (bg, fg, spec->data.composite.alpha,
widget, drawable, clip, mode,
xalign, yalign,
x, y, width, height);
break;
case META_TEXTURE_GRADIENT:
draw_bg_gradient_composite (bg, fg, spec->data.composite.alpha,
widget, drawable, clip, mode,
xalign, yalign,
x, y, width, height);
break;
case META_TEXTURE_IMAGE:
draw_bg_image_composite (bg, fg, spec->data.composite.alpha,
widget, drawable, clip, mode,
xalign, yalign,
x, y, width, height);
break;
case META_TEXTURE_COMPOSITE:
g_assert_not_reached ();
break;
}
}
break;
}
}
MetaFrameStyle*
meta_frame_style_new (void)
{
MetaFrameStyle *style;
style = g_new0 (MetaFrameStyle, 1);
style->refcount = 1;
return style;
}
void
meta_frame_style_ref (MetaFrameStyle *style)
{
g_return_if_fail (style != NULL);
style->refcount += 1;
}
static void
free_button_textures (MetaTextureSpec *textures[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
{
int i, j;
i = 0;
while (i < META_BUTTON_TYPE_LAST)
{
j = 0;
while (j < META_BUTTON_STATE_LAST)
{
if (textures[i][j])
meta_texture_spec_free (textures[i][j]);
++j;
}
++i;
}
}
void
meta_frame_style_unref (MetaFrameStyle *style)
{
g_return_if_fail (style != NULL);
g_return_if_fail (style->refcount > 0);
style->refcount -= 1;
if (style->refcount == 0)
{
int i;
free_button_textures (style->button_icons);
free_button_textures (style->button_backgrounds);
i = 0;
while (i < META_FRAME_PIECE_LAST)
{
if (style->pieces[i])
meta_texture_spec_free (style->pieces[i]);
++i;
}
if (style->layout)
meta_frame_layout_free (style->layout);
g_free (style);
}
}
MetaFrameStyleSet*
meta_frame_style_set_new (void)
{
MetaFrameStyleSet *style_set;
style_set = g_new0 (MetaFrameStyleSet, 1);
return style_set;
}
static void
free_focus_styles (MetaFrameStyle *focus_styles[META_WINDOW_FOCUS_LAST])
{
int i;
i = 0;
while (i < META_WINDOW_FOCUS_LAST)
{
if (focus_styles[i])
meta_frame_style_unref (focus_styles[i]);
++i;
}
}
void
meta_frame_style_set_free (MetaFrameStyleSet *style_set)
{
int i;
i = 0;
while (i < META_WINDOW_RESIZE_LAST)
{
free_focus_styles (style_set->normal_styles[i]);
++i;
}
free_focus_styles (style_set->maximized_styles);
free_focus_styles (style_set->shaded_styles);
free_focus_styles (style_set->maximized_and_shaded_styles);
g_free (style_set);
}