summaryrefslogtreecommitdiff
path: root/libmetacity
diff options
context:
space:
mode:
authorAlberts Muktupāvels <alberts.muktupavels@gmail.com>2016-01-29 02:10:53 +0200
committerAlberts Muktupāvels <alberts.muktupavels@gmail.com>2016-01-29 02:10:53 +0200
commita29c9041b4b4edb2bef3a0a48a88522b24883662 (patch)
tree19d70149879e11fb0b275440b72ea9c25fb15dd7 /libmetacity
parent50bb3d8ef169ab1ba39af4f1a007144132fb569c (diff)
downloadmetacity-a29c9041b4b4edb2bef3a0a48a88522b24883662.tar.gz
libmetacity: add meta-draw-op.[c/h]
Diffstat (limited to 'libmetacity')
-rw-r--r--libmetacity/Makefile.am3
-rw-r--r--libmetacity/meta-draw-op.c1417
-rw-r--r--libmetacity/meta-draw-op.h266
3 files changed, 1686 insertions, 0 deletions
diff --git a/libmetacity/Makefile.am b/libmetacity/Makefile.am
index 54dde93a..88679d90 100644
--- a/libmetacity/Makefile.am
+++ b/libmetacity/Makefile.am
@@ -12,6 +12,8 @@ libmetacity_la_SOURCES = \
meta-color-private.h \
meta-color-spec.c \
meta-color-spec.h \
+ meta-draw-op.c \
+ meta-draw-op.h \
meta-draw-spec.c \
meta-draw-spec.h \
meta-frame-borders.c \
@@ -63,6 +65,7 @@ libmetacity_include_HEADERS = \
meta-button-layout.h \
meta-color.h \
meta-color-spec.h \
+ meta-draw-op.h \
meta-draw-spec.h \
meta-frame-borders.h \
meta-frame-flags.h \
diff --git a/libmetacity/meta-draw-op.c b/libmetacity/meta-draw-op.c
new file mode 100644
index 00000000..e57e595d
--- /dev/null
+++ b/libmetacity/meta-draw-op.c
@@ -0,0 +1,1417 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <math.h>
+
+#include "meta-draw-op.h"
+
+#define GDK_COLOR_RGBA(color) \
+ ((guint32) (0xff | \
+ ((int)((color).red * 255) << 24) | \
+ ((int)((color).green * 255) << 16) | \
+ ((int)((color).blue * 255) << 8)))
+
+#define GDK_COLOR_RGB(color) \
+ ((guint32) (((int)((color).red * 255) << 16) | \
+ ((int)((color).green * 255) << 8) | \
+ ((int)((color).blue * 255))))
+
+#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
+#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
+
+/**
+ * A list of MetaDrawOp objects. Maintains a reference count.
+ * Grows as necessary and allows the allocation of unused spaces
+ * to keep reallocations to a minimum.
+ *
+ * \bug Do we really win anything from not using the equivalent
+ * GLib structures?
+ */
+struct _MetaDrawOpList
+{
+ int refcount;
+ MetaDrawOp **ops;
+ int n_ops;
+ int n_allocated;
+};
+
+static void
+fill_env (MetaPositionExprEnv *env,
+ const MetaDrawInfo *info,
+ GdkRectangle logical_region)
+{
+ /* FIXME this stuff could be raised into draw_op_list_draw() probably
+ */
+ env->rect = logical_region;
+ env->object_width = -1;
+ env->object_height = -1;
+
+ env->left_width = info->borders.visible.left;
+ env->right_width = info->borders.visible.right;
+ env->top_height = info->borders.visible.top;
+ env->bottom_height = info->borders.visible.bottom;
+ env->frame_x_center = info->width / 2 - logical_region.x;
+ env->frame_y_center = info->height / 2 - logical_region.y;
+
+ env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
+ env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
+ env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
+ env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
+
+ env->title_width = info->title_layout_width;
+ env->title_height = info->title_layout_height;
+}
+
+static GdkPixbuf*
+pixbuf_tile (GdkPixbuf *tile,
+ int width,
+ int height)
+{
+ GdkPixbuf *pixbuf;
+ int tile_width;
+ int tile_height;
+ int i, j;
+
+ tile_width = gdk_pixbuf_get_width (tile);
+ tile_height = gdk_pixbuf_get_height (tile);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ gdk_pixbuf_get_has_alpha (tile),
+ 8, width, height);
+
+ i = 0;
+ while (i < width)
+ {
+ j = 0;
+ while (j < height)
+ {
+ int w, h;
+
+ w = MIN (tile_width, width - i);
+ h = MIN (tile_height, height - j);
+
+ gdk_pixbuf_copy_area (tile,
+ 0, 0,
+ w, h,
+ pixbuf,
+ i, j);
+
+ j += tile_height;
+ }
+
+ i += tile_width;
+ }
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+replicate_rows (GdkPixbuf *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
+ unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
+ unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
+ * n_channels);
+ unsigned char *dest_pixels;
+ GdkPixbuf *result;
+ unsigned int dest_rowstride;
+ int i;
+
+ result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
+ width, height);
+ dest_rowstride = gdk_pixbuf_get_rowstride (result);
+ dest_pixels = gdk_pixbuf_get_pixels (result);
+
+ for (i = 0; i < height; i++)
+ memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
+
+ return result;
+}
+
+static GdkPixbuf *
+replicate_cols (GdkPixbuf *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
+ unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
+ unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
+ * n_channels);
+ unsigned char *dest_pixels;
+ GdkPixbuf *result;
+ unsigned int dest_rowstride;
+ int i, j;
+
+ result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
+ width, height);
+ dest_rowstride = gdk_pixbuf_get_rowstride (result);
+ dest_pixels = gdk_pixbuf_get_pixels (result);
+
+ for (i = 0; i < height; i++)
+ {
+ unsigned char *p = dest_pixels + dest_rowstride * i;
+ unsigned char *q = pixels + src_rowstride * i;
+
+ unsigned char r = *(q++);
+ unsigned char g = *(q++);
+ unsigned char b = *(q++);
+
+ if (n_channels == 4)
+ {
+ unsigned char a;
+
+ a = *(q++);
+
+ for (j = 0; j < width; j++)
+ {
+ *(p++) = r;
+ *(p++) = g;
+ *(p++) = b;
+ *(p++) = a;
+ }
+ }
+ else
+ {
+ for (j = 0; j < width; j++)
+ {
+ *(p++) = r;
+ *(p++) = g;
+ *(p++) = b;
+ }
+ }
+ }
+
+ return result;
+}
+
+static GdkPixbuf*
+scale_and_alpha_pixbuf (GdkPixbuf *src,
+ MetaAlphaGradientSpec *alpha_spec,
+ MetaImageFillType fill_type,
+ int width,
+ int height,
+ gboolean vertical_stripes,
+ gboolean horizontal_stripes)
+{
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *temp_pixbuf;
+
+ pixbuf = NULL;
+
+ pixbuf = src;
+
+ if (gdk_pixbuf_get_width (pixbuf) == width &&
+ gdk_pixbuf_get_height (pixbuf) == height)
+ {
+ g_object_ref (G_OBJECT (pixbuf));
+ }
+ else
+ {
+ if (fill_type == META_IMAGE_FILL_TILE)
+ {
+ pixbuf = pixbuf_tile (pixbuf, width, height);
+ }
+ else
+ {
+ int src_h, src_w, dest_h, dest_w;
+ src_h = gdk_pixbuf_get_height (src);
+ src_w = gdk_pixbuf_get_width (src);
+
+ /* prefer to replicate_cols if possible, as that
+ * is faster (no memory reads)
+ */
+ if (horizontal_stripes)
+ {
+ dest_w = gdk_pixbuf_get_width (src);
+ dest_h = height;
+ }
+ else if (vertical_stripes)
+ {
+ dest_w = width;
+ dest_h = gdk_pixbuf_get_height (src);
+ }
+
+ else
+ {
+ dest_w = width;
+ dest_h = height;
+ }
+
+ if (dest_w == src_w && dest_h == src_h)
+ {
+ temp_pixbuf = src;
+ g_object_ref (G_OBJECT (temp_pixbuf));
+ }
+ else
+ {
+ temp_pixbuf = gdk_pixbuf_scale_simple (src,
+ dest_w, dest_h,
+ GDK_INTERP_BILINEAR);
+ }
+
+ /* prefer to replicate_cols if possible, as that
+ * is faster (no memory reads)
+ */
+ if (horizontal_stripes)
+ {
+ pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
+ g_object_unref (G_OBJECT (temp_pixbuf));
+ }
+ else if (vertical_stripes)
+ {
+ pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
+ g_object_unref (G_OBJECT (temp_pixbuf));
+ }
+ else
+ {
+ pixbuf = temp_pixbuf;
+ }
+ }
+ }
+
+ if (pixbuf)
+ pixbuf = meta_alpha_gradient_spec_apply_alpha (alpha_spec, pixbuf, pixbuf == src);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+colorize_pixbuf (GdkPixbuf *orig,
+ GdkRGBA *new_color)
+{
+ GdkPixbuf *pixbuf;
+ double intensity;
+ int x, y;
+ const guchar *src;
+ guchar *dest;
+ int orig_rowstride;
+ int dest_rowstride;
+ int width, height;
+ gboolean has_alpha;
+ const guchar *src_pixels;
+ guchar *dest_pixels;
+
+ pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
+ gdk_pixbuf_get_bits_per_sample (orig),
+ gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
+
+ if (pixbuf == NULL)
+ return NULL;
+
+ orig_rowstride = gdk_pixbuf_get_rowstride (orig);
+ dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ has_alpha = gdk_pixbuf_get_has_alpha (orig);
+ src_pixels = gdk_pixbuf_get_pixels (orig);
+ dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ for (y = 0; y < height; y++)
+ {
+ src = src_pixels + y * orig_rowstride;
+ dest = dest_pixels + y * dest_rowstride;
+
+ for (x = 0; x < width; x++)
+ {
+ double dr, dg, db;
+
+ intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
+
+ if (intensity <= 0.5)
+ {
+ /* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
+ dr = new_color->red * intensity * 2.0;
+ dg = new_color->green * intensity * 2.0;
+ db = new_color->blue * intensity * 2.0;
+ }
+ else
+ {
+ /* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
+ dr = new_color->red + (1.0 - new_color->red) * (intensity - 0.5) * 2.0;
+ dg = new_color->green + (1.0 - new_color->green) * (intensity - 0.5) * 2.0;
+ db = new_color->blue + (1.0 - new_color->blue) * (intensity - 0.5) * 2.0;
+ }
+
+ dest[0] = CLAMP_UCHAR (255 * dr);
+ dest[1] = CLAMP_UCHAR (255 * dg);
+ dest[2] = CLAMP_UCHAR (255 * db);
+
+ if (has_alpha)
+ {
+ dest[3] = src[3];
+ src += 4;
+ dest += 4;
+ }
+ else
+ {
+ src += 3;
+ dest += 3;
+ }
+ }
+ }
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+draw_op_as_pixbuf (const MetaDrawOp *op,
+ GtkStyleContext *context,
+ const MetaDrawInfo *info,
+ int width,
+ int height)
+{
+ /* Try to get the op as a pixbuf, assuming w/h in the op
+ * matches the width/height passed in. return NULL
+ * if the op can't be converted to an equivalent pixbuf.
+ */
+ GdkPixbuf *pixbuf;
+
+ pixbuf = NULL;
+
+ switch (op->type)
+ {
+ case META_DRAW_LINE:
+ break;
+
+ case META_DRAW_RECTANGLE:
+ if (op->data.rectangle.filled)
+ {
+ GdkRGBA color;
+
+ meta_color_spec_render (op->data.rectangle.color_spec,
+ context,
+ &color);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ FALSE,
+ 8, width, height);
+
+ gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
+ }
+ break;
+
+ case META_DRAW_ARC:
+ break;
+
+ case META_DRAW_CLIP:
+ break;
+
+ case META_DRAW_TINT:
+ {
+ GdkRGBA color;
+
+ meta_color_spec_render (op->data.rectangle.color_spec,
+ context, &color);
+
+ pixbuf = meta_alpha_gradient_spec_render (op->data.tint.alpha_spec,
+ width, height, color);
+ }
+ break;
+
+ case META_DRAW_GRADIENT:
+ {
+ pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
+ context, width, height);
+
+ pixbuf = meta_alpha_gradient_spec_apply_alpha (op->data.gradient.alpha_spec,
+ pixbuf, FALSE);
+ }
+ break;
+
+ case META_DRAW_IMAGE:
+ {
+ if (op->data.image.colorize_spec)
+ {
+ GdkRGBA color;
+
+ meta_color_spec_render (op->data.image.colorize_spec,
+ context, &color);
+
+ if (op->data.image.colorize_cache_pixbuf == NULL ||
+ op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
+ {
+ if (op->data.image.colorize_cache_pixbuf)
+ g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
+
+ /* const cast here */
+ ((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
+ colorize_pixbuf (op->data.image.pixbuf,
+ &color);
+ ((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
+ GDK_COLOR_RGB (color);
+ }
+
+ if (op->data.image.colorize_cache_pixbuf)
+ {
+ pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
+ op->data.image.alpha_spec,
+ op->data.image.fill_type,
+ width, height,
+ op->data.image.vertical_stripes,
+ op->data.image.horizontal_stripes);
+ }
+ }
+ else
+ {
+ pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
+ op->data.image.alpha_spec,
+ op->data.image.fill_type,
+ width, height,
+ op->data.image.vertical_stripes,
+ op->data.image.horizontal_stripes);
+ }
+ break;
+ }
+
+ case META_DRAW_GTK_ARROW:
+ case META_DRAW_GTK_BOX:
+ case META_DRAW_GTK_VLINE:
+ break;
+
+ case META_DRAW_ICON:
+ if (info->mini_icon &&
+ width <= gdk_pixbuf_get_width (info->mini_icon) &&
+ height <= gdk_pixbuf_get_height (info->mini_icon))
+ pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
+ op->data.icon.alpha_spec,
+ op->data.icon.fill_type,
+ width, height,
+ FALSE, FALSE);
+ else if (info->icon)
+ pixbuf = scale_and_alpha_pixbuf (info->icon,
+ op->data.icon.alpha_spec,
+ op->data.icon.fill_type,
+ width, height,
+ FALSE, FALSE);
+ break;
+
+ case META_DRAW_TITLE:
+ break;
+
+ case META_DRAW_OP_LIST:
+ break;
+
+ case META_DRAW_TILE:
+ break;
+
+ default:
+ break;
+ }
+
+ return pixbuf;
+}
+
+/* This code was originally rendering anti-aliased using X primitives, and
+ * now has been switched to draw anti-aliased using cairo. In general, the
+ * closest correspondence between X rendering and cairo rendering is given
+ * by offsetting the geometry by 0.5 pixels in both directions before rendering
+ * with cairo. This is because X samples at the upper left corner of the
+ * pixel while cairo averages over the entire pixel. However, in the cases
+ * where the X rendering was an exact rectangle with no "jaggies"
+ * we need to be a bit careful about applying the offset. We want to produce
+ * the exact same pixel-aligned rectangle, rather than a rectangle with
+ * fuzz around the edges.
+ */
+static void
+draw_op_draw_with_env (const MetaDrawOp *op,
+ GtkStyleContext *context,
+ cairo_t *cr,
+ const MetaDrawInfo *info,
+ GdkRectangle rect,
+ MetaPositionExprEnv *env)
+{
+ GdkRGBA color;
+
+ cairo_save (cr);
+
+ cairo_set_line_width (cr, 1.0);
+
+ switch (op->type)
+ {
+ case META_DRAW_LINE:
+ {
+ int x1, x2, y1, y2;
+
+ meta_color_spec_render (op->data.line.color_spec, context, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ if (op->data.line.width > 0)
+ cairo_set_line_width (cr, op->data.line.width);
+
+ if (op->data.line.dash_on_length > 0 &&
+ op->data.line.dash_off_length > 0)
+ {
+ double dash_list[2];
+ dash_list[0] = op->data.line.dash_on_length;
+ dash_list[1] = op->data.line.dash_off_length;
+ cairo_set_dash (cr, dash_list, 2, 0);
+ }
+
+ x1 = meta_draw_spec_parse_x_position (op->data.line.x1, env);
+ y1 = meta_draw_spec_parse_y_position (op->data.line.y1, env);
+
+ if (!op->data.line.x2 &&
+ !op->data.line.y2 &&
+ op->data.line.width==0)
+ {
+ cairo_rectangle (cr, x1, y1, 1, 1);
+ cairo_fill (cr);
+ }
+ else
+ {
+ if (op->data.line.x2)
+ x2 = meta_draw_spec_parse_x_position (op->data.line.x2, env);
+ else
+ x2 = x1;
+
+ if (op->data.line.y2)
+ y2 = meta_draw_spec_parse_y_position (op->data.line.y2, env);
+ else
+ y2 = y1;
+
+ /* This is one of the cases where we are matching the exact
+ * pixel aligned rectangle produced by X; for zero-width lines
+ * the generic algorithm produces the right result so we don't
+ * need to handle them here.
+ */
+ if ((y1 == y2 || x1 == x2) && op->data.line.width != 0)
+ {
+ double offset = op->data.line.width % 2 ? .5 : 0;
+
+ if (y1 == y2)
+ {
+ cairo_move_to (cr, x1, y1 + offset);
+ cairo_line_to (cr, x2, y2 + offset);
+ }
+ else
+ {
+ cairo_move_to (cr, x1 + offset, y1);
+ cairo_line_to (cr, x2 + offset, y2);
+ }
+ }
+ else
+ {
+ /* zero-width lines include both end-points in X, unlike wide lines */
+ if (op->data.line.width == 0)
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+ cairo_move_to (cr, x1 + .5, y1 + .5);
+ cairo_line_to (cr, x2 + .5, y2 + .5);
+ }
+ cairo_stroke (cr);
+ }
+ }
+ break;
+
+ case META_DRAW_RECTANGLE:
+ {
+ int rx, ry, rwidth, rheight;
+
+ meta_color_spec_render (op->data.rectangle.color_spec, context, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ rx = meta_draw_spec_parse_x_position (op->data.rectangle.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.rectangle.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.rectangle.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.rectangle.height, env);
+
+ /* Filled and stroked rectangles are the other cases
+ * we pixel-align to X rasterization
+ */
+ if (op->data.rectangle.filled)
+ {
+ cairo_rectangle (cr, rx, ry, rwidth, rheight);
+ cairo_fill (cr);
+ }
+ else
+ {
+ cairo_rectangle (cr, rx + .5, ry + .5, rwidth, rheight);
+ cairo_stroke (cr);
+ }
+ }
+ break;
+
+ case META_DRAW_ARC:
+ {
+ int rx, ry, rwidth, rheight;
+ double start_angle, end_angle;
+ double center_x, center_y;
+
+ meta_color_spec_render (op->data.arc.color_spec, context, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ rx = meta_draw_spec_parse_x_position (op->data.arc.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.arc.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.arc.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.arc.height, env);
+
+ start_angle = op->data.arc.start_angle * (M_PI / 180.)
+ - (.5 * M_PI); /* start at 12 instead of 3 oclock */
+ end_angle = start_angle + op->data.arc.extent_angle * (M_PI / 180.);
+ center_x = rx + (double)rwidth / 2. + .5;
+ center_y = ry + (double)rheight / 2. + .5;
+
+ cairo_save (cr);
+
+ cairo_translate (cr, center_x, center_y);
+ cairo_scale (cr, (double)rwidth / 2., (double)rheight / 2.);
+
+ if (op->data.arc.extent_angle >= 0)
+ cairo_arc (cr, 0, 0, 1, start_angle, end_angle);
+ else
+ cairo_arc_negative (cr, 0, 0, 1, start_angle, end_angle);
+
+ cairo_restore (cr);
+
+ if (op->data.arc.filled)
+ {
+ cairo_line_to (cr, center_x, center_y);
+ cairo_fill (cr);
+ }
+ else
+ cairo_stroke (cr);
+ }
+ break;
+
+ case META_DRAW_CLIP:
+ break;
+
+ case META_DRAW_TINT:
+ {
+ int rx, ry, rwidth, rheight;
+ gboolean needs_alpha;
+
+ needs_alpha = meta_alpha_gradient_spec_needs_alpha (op->data.tint.alpha_spec);
+
+ rx = meta_draw_spec_parse_x_position (op->data.tint.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.tint.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.tint.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.tint.height, env);
+
+ if (!needs_alpha)
+ {
+ meta_color_spec_render (op->data.tint.color_spec, context, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ cairo_rectangle (cr, rx, ry, rwidth, rheight);
+ cairo_fill (cr);
+ }
+ else
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = draw_op_as_pixbuf (op, context, info, rwidth, rheight);
+
+ if (pixbuf)
+ {
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
+ cairo_paint (cr);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ }
+ }
+ break;
+
+ case META_DRAW_GRADIENT:
+ {
+ int rx, ry, rwidth, rheight;
+ GdkPixbuf *pixbuf;
+
+ rx = meta_draw_spec_parse_x_position (op->data.gradient.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gradient.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gradient.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gradient.height, env);
+
+ pixbuf = draw_op_as_pixbuf (op, context, info, rwidth, rheight);
+
+ if (pixbuf)
+ {
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
+ cairo_paint (cr);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ }
+ break;
+
+ case META_DRAW_IMAGE:
+ {
+ int rx, ry, rwidth, rheight;
+ GdkPixbuf *pixbuf;
+
+ if (op->data.image.pixbuf)
+ {
+ env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
+ env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
+ }
+
+ rwidth = meta_draw_spec_parse_size (op->data.image.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.image.height, env);
+
+ pixbuf = draw_op_as_pixbuf (op, context, info, rwidth, rheight);
+
+ if (pixbuf)
+ {
+ rx = meta_draw_spec_parse_x_position (op->data.image.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.image.y, env);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
+ cairo_paint (cr);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ }
+ break;
+
+ case META_DRAW_GTK_ARROW:
+ {
+ int rx, ry, rwidth, rheight;
+ double angle = 0, size;
+
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_arrow.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gtk_arrow.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gtk_arrow.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gtk_arrow.height, env);
+
+ size = MAX(rwidth, rheight);
+
+ switch (op->data.gtk_arrow.arrow)
+ {
+ case GTK_ARROW_UP:
+ angle = 0;
+ break;
+ case GTK_ARROW_RIGHT:
+ angle = M_PI / 2;
+ break;
+ case GTK_ARROW_DOWN:
+ angle = M_PI;
+ break;
+ case GTK_ARROW_LEFT:
+ angle = 3 * M_PI / 2;
+ break;
+ case GTK_ARROW_NONE:
+ return;
+ default:
+ break;
+ }
+
+ gtk_style_context_set_state (context, op->data.gtk_arrow.state);
+ gtk_render_arrow (context, cr, angle, rx, ry, size);
+ }
+ break;
+
+ case META_DRAW_GTK_BOX:
+ {
+ int rx, ry, rwidth, rheight;
+
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_box.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.gtk_box.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.gtk_box.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.gtk_box.height, env);
+
+ gtk_style_context_set_state (context, op->data.gtk_box.state);
+ gtk_render_background (context, cr, rx, ry, rwidth, rheight);
+ gtk_render_frame (context, cr, rx, ry, rwidth, rheight);
+ }
+ break;
+
+ case META_DRAW_GTK_VLINE:
+ {
+ int rx, ry1, ry2;
+
+ rx = meta_draw_spec_parse_x_position (op->data.gtk_vline.x, env);
+ ry1 = meta_draw_spec_parse_y_position (op->data.gtk_vline.y1, env);
+ ry2 = meta_draw_spec_parse_y_position (op->data.gtk_vline.y2, env);
+
+ gtk_style_context_set_state (context, op->data.gtk_vline.state);
+ gtk_render_line (context, cr, rx, ry1, rx, ry2);
+ }
+ break;
+
+ case META_DRAW_ICON:
+ {
+ int rx, ry, rwidth, rheight;
+ GdkPixbuf *pixbuf;
+
+ rwidth = meta_draw_spec_parse_size (op->data.icon.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.icon.height, env);
+
+ pixbuf = draw_op_as_pixbuf (op, context, info, rwidth, rheight);
+
+ if (pixbuf)
+ {
+ rx = meta_draw_spec_parse_x_position (op->data.icon.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.icon.y, env);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
+ cairo_paint (cr);
+
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+ }
+ break;
+
+ case META_DRAW_TITLE:
+ if (info->title_layout)
+ {
+ int rx, ry;
+ PangoRectangle ink_rect, logical_rect;
+
+ meta_color_spec_render (op->data.title.color_spec, context, &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ rx = meta_draw_spec_parse_x_position (op->data.title.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.title.y, env);
+
+ if (op->data.title.ellipsize_width)
+ {
+ int ellipsize_width;
+ int right_bearing;
+
+ ellipsize_width = meta_draw_spec_parse_x_position (op->data.title.ellipsize_width, env);
+ /* HACK: meta_draw_spec_parse_x_position adds in env->rect.x, subtract out again */
+ ellipsize_width -= env->rect.x;
+
+ pango_layout_set_width (info->title_layout, -1);
+ pango_layout_get_pixel_extents (info->title_layout,
+ &ink_rect, &logical_rect);
+
+ /* Pango's idea of ellipsization is with respect to the logical rect.
+ * correct for this, by reducing the ellipsization width by the overflow
+ * of the un-ellipsized text on the right... it's always the visual
+ * right we want regardless of bidi, since since the X we pass in to
+ * cairo_move_to() is always the left edge of the line.
+ */
+ right_bearing = (ink_rect.x + ink_rect.width) - (logical_rect.x + logical_rect.width);
+ right_bearing = MAX (right_bearing, 0);
+
+ ellipsize_width -= right_bearing;
+ ellipsize_width = MAX (ellipsize_width, 0);
+
+ /* Only ellipsizing when necessary is a performance optimization -
+ * pango_layout_set_width() will force a relayout if it isn't the
+ * same as the current width of -1.
+ */
+ if (ellipsize_width < logical_rect.width)
+ pango_layout_set_width (info->title_layout, PANGO_SCALE * ellipsize_width);
+ }
+ else if (rx - env->rect.x + env->title_width >= env->rect.width)
+ {
+ const double alpha_margin = 30.0;
+ int text_space = env->rect.x + env->rect.width -
+ (rx - env->rect.x) - env->right_width;
+
+ double startalpha = 1.0 - (alpha_margin/((double)text_space));
+
+ cairo_pattern_t *linpat;
+ linpat = cairo_pattern_create_linear (rx, ry, text_space,
+ env->title_height);
+ cairo_pattern_add_color_stop_rgba (linpat, 0, color.red,
+ color.green,
+ color.blue,
+ color.alpha);
+ cairo_pattern_add_color_stop_rgba (linpat, startalpha,
+ color.red,
+ color.green,
+ color.blue,
+ color.alpha);
+ cairo_pattern_add_color_stop_rgba (linpat, 1, color.red,
+ color.green,
+ color.blue, 0);
+ cairo_set_source(cr, linpat);
+ cairo_pattern_destroy(linpat);
+ }
+
+ cairo_move_to (cr, rx, ry);
+ pango_cairo_show_layout (cr, info->title_layout);
+
+ /* Remove any ellipsization we might have set; will short-circuit
+ * if the width is already -1 */
+ pango_layout_set_width (info->title_layout, -1);
+ }
+ break;
+
+ case META_DRAW_OP_LIST:
+ {
+ GdkRectangle d_rect;
+
+ d_rect.x = meta_draw_spec_parse_x_position (op->data.op_list.x, env);
+ d_rect.y = meta_draw_spec_parse_y_position (op->data.op_list.y, env);
+ d_rect.width = meta_draw_spec_parse_size (op->data.op_list.width, env);
+ d_rect.height = meta_draw_spec_parse_size (op->data.op_list.height, env);
+
+ meta_draw_op_list_draw_with_style (op->data.op_list.op_list,
+ context, cr, info, d_rect);
+ }
+ break;
+
+ case META_DRAW_TILE:
+ {
+ int rx, ry, rwidth, rheight;
+ int tile_xoffset, tile_yoffset;
+ GdkRectangle tile;
+
+ rx = meta_draw_spec_parse_x_position (op->data.tile.x, env);
+ ry = meta_draw_spec_parse_y_position (op->data.tile.y, env);
+ rwidth = meta_draw_spec_parse_size (op->data.tile.width, env);
+ rheight = meta_draw_spec_parse_size (op->data.tile.height, env);
+
+ cairo_save (cr);
+
+ cairo_rectangle (cr, rx, ry, rwidth, rheight);
+ cairo_clip (cr);
+
+ tile_xoffset = meta_draw_spec_parse_x_position (op->data.tile.tile_xoffset, env);
+ tile_yoffset = meta_draw_spec_parse_y_position (op->data.tile.tile_yoffset, env);
+ /* tile offset should not include x/y */
+ tile_xoffset -= rect.x;
+ tile_yoffset -= rect.y;
+
+ tile.width = meta_draw_spec_parse_size (op->data.tile.tile_width, env);
+ tile.height = meta_draw_spec_parse_size (op->data.tile.tile_height, env);
+
+ tile.x = rx - tile_xoffset;
+
+ while (tile.x < (rx + rwidth))
+ {
+ tile.y = ry - tile_yoffset;
+ while (tile.y < (ry + rheight))
+ {
+ meta_draw_op_list_draw_with_style (op->data.tile.op_list,
+ context, cr, info, tile);
+
+ tile.y += tile.height;
+ }
+
+ tile.x += tile.width;
+ }
+ cairo_restore (cr);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ cairo_restore (cr);
+}
+
+MetaDrawOp *
+meta_draw_op_new (MetaDrawType type)
+{
+ MetaDrawOp *op;
+ MetaDrawOp dummy;
+ int size;
+
+ size = G_STRUCT_OFFSET (MetaDrawOp, data);
+
+ switch (type)
+ {
+ case META_DRAW_LINE:
+ size += sizeof (dummy.data.line);
+ break;
+
+ case META_DRAW_RECTANGLE:
+ size += sizeof (dummy.data.rectangle);
+ break;
+
+ case META_DRAW_ARC:
+ size += sizeof (dummy.data.arc);
+ break;
+
+ case META_DRAW_CLIP:
+ size += sizeof (dummy.data.clip);
+ break;
+
+ case META_DRAW_TINT:
+ size += sizeof (dummy.data.tint);
+ break;
+
+ case META_DRAW_GRADIENT:
+ size += sizeof (dummy.data.gradient);
+ break;
+
+ case META_DRAW_IMAGE:
+ size += sizeof (dummy.data.image);
+ break;
+
+ case META_DRAW_GTK_ARROW:
+ size += sizeof (dummy.data.gtk_arrow);
+ break;
+
+ case META_DRAW_GTK_BOX:
+ size += sizeof (dummy.data.gtk_box);
+ break;
+
+ case META_DRAW_GTK_VLINE:
+ size += sizeof (dummy.data.gtk_vline);
+ break;
+
+ case META_DRAW_ICON:
+ size += sizeof (dummy.data.icon);
+ break;
+
+ case META_DRAW_TITLE:
+ size += sizeof (dummy.data.title);
+ break;
+ case META_DRAW_OP_LIST:
+ size += sizeof (dummy.data.op_list);
+ break;
+ case META_DRAW_TILE:
+ size += sizeof (dummy.data.tile);
+ break;
+
+ default:
+ break;
+ }
+
+ op = g_malloc0 (size);
+
+ op->type = type;
+
+ return op;
+}
+
+void
+meta_draw_op_free (MetaDrawOp *op)
+{
+ g_return_if_fail (op != NULL);
+
+ switch (op->type)
+ {
+ case META_DRAW_LINE:
+ if (op->data.line.color_spec)
+ meta_color_spec_free (op->data.line.color_spec);
+
+ meta_draw_spec_free (op->data.line.x1);
+ meta_draw_spec_free (op->data.line.y1);
+ meta_draw_spec_free (op->data.line.x2);
+ meta_draw_spec_free (op->data.line.y2);
+ break;
+
+ case META_DRAW_RECTANGLE:
+ if (op->data.rectangle.color_spec)
+ g_free (op->data.rectangle.color_spec);
+
+ meta_draw_spec_free (op->data.rectangle.x);
+ meta_draw_spec_free (op->data.rectangle.y);
+ meta_draw_spec_free (op->data.rectangle.width);
+ meta_draw_spec_free (op->data.rectangle.height);
+ break;
+
+ case META_DRAW_ARC:
+ if (op->data.arc.color_spec)
+ g_free (op->data.arc.color_spec);
+
+ meta_draw_spec_free (op->data.arc.x);
+ meta_draw_spec_free (op->data.arc.y);
+ meta_draw_spec_free (op->data.arc.width);
+ meta_draw_spec_free (op->data.arc.height);
+ break;
+
+ case META_DRAW_CLIP:
+ meta_draw_spec_free (op->data.clip.x);
+ meta_draw_spec_free (op->data.clip.y);
+ meta_draw_spec_free (op->data.clip.width);
+ meta_draw_spec_free (op->data.clip.height);
+ break;
+
+ case META_DRAW_TINT:
+ if (op->data.tint.color_spec)
+ meta_color_spec_free (op->data.tint.color_spec);
+
+ if (op->data.tint.alpha_spec)
+ meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
+
+ meta_draw_spec_free (op->data.tint.x);
+ meta_draw_spec_free (op->data.tint.y);
+ meta_draw_spec_free (op->data.tint.width);
+ meta_draw_spec_free (op->data.tint.height);
+ break;
+
+ case META_DRAW_GRADIENT:
+ if (op->data.gradient.gradient_spec)
+ meta_gradient_spec_free (op->data.gradient.gradient_spec);
+
+ if (op->data.gradient.alpha_spec)
+ meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
+
+ meta_draw_spec_free (op->data.gradient.x);
+ meta_draw_spec_free (op->data.gradient.y);
+ meta_draw_spec_free (op->data.gradient.width);
+ meta_draw_spec_free (op->data.gradient.height);
+ break;
+
+ case META_DRAW_IMAGE:
+ if (op->data.image.alpha_spec)
+ meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
+
+ if (op->data.image.pixbuf)
+ g_object_unref (G_OBJECT (op->data.image.pixbuf));
+
+ if (op->data.image.colorize_spec)
+ meta_color_spec_free (op->data.image.colorize_spec);
+
+ if (op->data.image.colorize_cache_pixbuf)
+ g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
+
+ meta_draw_spec_free (op->data.image.x);
+ meta_draw_spec_free (op->data.image.y);
+ meta_draw_spec_free (op->data.image.width);
+ meta_draw_spec_free (op->data.image.height);
+ break;
+
+ case META_DRAW_GTK_ARROW:
+ meta_draw_spec_free (op->data.gtk_arrow.x);
+ meta_draw_spec_free (op->data.gtk_arrow.y);
+ meta_draw_spec_free (op->data.gtk_arrow.width);
+ meta_draw_spec_free (op->data.gtk_arrow.height);
+ break;
+
+ case META_DRAW_GTK_BOX:
+ meta_draw_spec_free (op->data.gtk_box.x);
+ meta_draw_spec_free (op->data.gtk_box.y);
+ meta_draw_spec_free (op->data.gtk_box.width);
+ meta_draw_spec_free (op->data.gtk_box.height);
+ break;
+
+ case META_DRAW_GTK_VLINE:
+ meta_draw_spec_free (op->data.gtk_vline.x);
+ meta_draw_spec_free (op->data.gtk_vline.y1);
+ meta_draw_spec_free (op->data.gtk_vline.y2);
+ break;
+
+ case META_DRAW_ICON:
+ if (op->data.icon.alpha_spec)
+ meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
+
+ meta_draw_spec_free (op->data.icon.x);
+ meta_draw_spec_free (op->data.icon.y);
+ meta_draw_spec_free (op->data.icon.width);
+ meta_draw_spec_free (op->data.icon.height);
+ break;
+
+ case META_DRAW_TITLE:
+ if (op->data.title.color_spec)
+ meta_color_spec_free (op->data.title.color_spec);
+
+ meta_draw_spec_free (op->data.title.x);
+ meta_draw_spec_free (op->data.title.y);
+ if (op->data.title.ellipsize_width)
+ meta_draw_spec_free (op->data.title.ellipsize_width);
+ break;
+
+ case META_DRAW_OP_LIST:
+ if (op->data.op_list.op_list)
+ meta_draw_op_list_unref (op->data.op_list.op_list);
+
+ meta_draw_spec_free (op->data.op_list.x);
+ meta_draw_spec_free (op->data.op_list.y);
+ meta_draw_spec_free (op->data.op_list.width);
+ meta_draw_spec_free (op->data.op_list.height);
+ break;
+
+ case META_DRAW_TILE:
+ if (op->data.tile.op_list)
+ meta_draw_op_list_unref (op->data.tile.op_list);
+
+ meta_draw_spec_free (op->data.tile.x);
+ meta_draw_spec_free (op->data.tile.y);
+ meta_draw_spec_free (op->data.tile.width);
+ meta_draw_spec_free (op->data.tile.height);
+ meta_draw_spec_free (op->data.tile.tile_xoffset);
+ meta_draw_spec_free (op->data.tile.tile_yoffset);
+ meta_draw_spec_free (op->data.tile.tile_width);
+ meta_draw_spec_free (op->data.tile.tile_height);
+ break;
+
+ default:
+ break;
+ }
+
+ g_free (op);
+}
+
+MetaDrawOpList *
+meta_draw_op_list_new (int n_preallocs)
+{
+ MetaDrawOpList *op_list;
+
+ g_return_val_if_fail (n_preallocs >= 0, NULL);
+
+ op_list = g_new (MetaDrawOpList, 1);
+
+ op_list->refcount = 1;
+ op_list->n_allocated = n_preallocs;
+ op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
+ op_list->n_ops = 0;
+
+ return op_list;
+}
+
+void
+meta_draw_op_list_ref (MetaDrawOpList *op_list)
+{
+ g_return_if_fail (op_list != NULL);
+
+ op_list->refcount += 1;
+}
+
+void
+meta_draw_op_list_unref (MetaDrawOpList *op_list)
+{
+ g_return_if_fail (op_list != NULL);
+ g_return_if_fail (op_list->refcount > 0);
+
+ op_list->refcount -= 1;
+
+ if (op_list->refcount == 0)
+ {
+ int i;
+
+ for (i = 0; i < op_list->n_ops; i++)
+ meta_draw_op_free (op_list->ops[i]);
+
+ g_free (op_list->ops);
+
+ g_free (op_list);
+ }
+}
+
+void
+meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list,
+ GtkStyleContext *context,
+ cairo_t *cr,
+ const MetaDrawInfo *info,
+ GdkRectangle rect)
+{
+ int i;
+ MetaPositionExprEnv env;
+
+ fill_env (&env, info, rect);
+
+ /* FIXME this can be optimized, potentially a lot, by
+ * compressing multiple ops when possible. For example,
+ * anything convertible to a pixbuf can be composited
+ * client-side, and putting a color tint over a pixbuf
+ * can be done without creating the solid-color pixbuf.
+ *
+ * To implement this my plan is to have the idea of a
+ * compiled draw op (with the string expressions already
+ * evaluated), we make an array of those, and then fold
+ * adjacent items when possible.
+ */
+ cairo_save (cr);
+
+ for (i = 0; i < op_list->n_ops; i++)
+ {
+ MetaDrawOp *op = op_list->ops[i];
+
+ if (op->type == META_DRAW_CLIP)
+ {
+ cairo_restore (cr);
+
+ cairo_rectangle (cr,
+ meta_draw_spec_parse_x_position (op->data.clip.x, &env),
+ meta_draw_spec_parse_y_position (op->data.clip.y, &env),
+ meta_draw_spec_parse_size (op->data.clip.width, &env),
+ meta_draw_spec_parse_size (op->data.clip.height, &env));
+ cairo_clip (cr);
+
+ cairo_save (cr);
+ }
+ else if (gdk_cairo_get_clip_rectangle (cr, NULL))
+ {
+ draw_op_draw_with_env (op, context, cr, info, rect, &env);
+ }
+ }
+
+ cairo_restore (cr);
+}
+
+void
+meta_draw_op_list_append (MetaDrawOpList *op_list,
+ MetaDrawOp *op)
+{
+ if (op_list->n_ops == op_list->n_allocated)
+ {
+ op_list->n_allocated *= 2;
+ op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
+ }
+
+ op_list->ops[op_list->n_ops] = op;
+ op_list->n_ops += 1;
+}
+
+gboolean
+meta_draw_op_list_validate (MetaDrawOpList *op_list,
+ GError **error)
+{
+ g_return_val_if_fail (op_list != NULL, FALSE);
+
+ /* empty lists are OK, nothing else to check really */
+
+ return TRUE;
+}
+
+/* This is not done in validate, since we wouldn't know the name
+ * of the list to report the error. It might be nice to
+ * store names inside the list sometime.
+ */
+gboolean
+meta_draw_op_list_contains (MetaDrawOpList *op_list,
+ MetaDrawOpList *child)
+{
+ int i;
+
+ /* mmm, huge tree recursion */
+
+ for (i = 0; i < op_list->n_ops; i++)
+ {
+ if (op_list->ops[i]->type == META_DRAW_OP_LIST)
+ {
+ if (op_list->ops[i]->data.op_list.op_list == child)
+ return TRUE;
+
+ if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
+ child))
+ return TRUE;
+ }
+ else if (op_list->ops[i]->type == META_DRAW_TILE)
+ {
+ if (op_list->ops[i]->data.tile.op_list == child)
+ return TRUE;
+
+ if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
+ child))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/libmetacity/meta-draw-op.h b/libmetacity/meta-draw-op.h
new file mode 100644
index 00000000..3042957a
--- /dev/null
+++ b/libmetacity/meta-draw-op.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef META_DRAW_OP_H
+#define META_DRAW_OP_H
+
+#include <gtk/gtk.h>
+#include <libmetacity/meta-color-spec.h>
+#include <libmetacity/meta-draw-spec.h>
+#include <libmetacity/meta-frame-borders.h>
+#include <libmetacity/meta-gradient-spec.h>
+
+G_BEGIN_DECLS
+
+typedef struct _MetaDrawInfo MetaDrawInfo;
+typedef struct _MetaDrawOp MetaDrawOp;
+typedef struct _MetaDrawOpList MetaDrawOpList;
+
+/**
+ * A drawing operation in our simple vector drawing language.
+ */
+typedef enum
+{
+ /** Basic drawing-- line */
+ META_DRAW_LINE,
+ /** Basic drawing-- rectangle */
+ META_DRAW_RECTANGLE,
+ /** Basic drawing-- arc */
+ META_DRAW_ARC,
+
+ /** Clip to a rectangle */
+ META_DRAW_CLIP,
+
+ /* Texture thingies */
+
+ /** Just a filled rectangle with alpha */
+ META_DRAW_TINT,
+ META_DRAW_GRADIENT,
+ META_DRAW_IMAGE,
+
+ /** GTK theme engine stuff */
+ META_DRAW_GTK_ARROW,
+ META_DRAW_GTK_BOX,
+ META_DRAW_GTK_VLINE,
+
+ /** App's window icon */
+ META_DRAW_ICON,
+ /** App's window title */
+ META_DRAW_TITLE,
+ /** a draw op list */
+ META_DRAW_OP_LIST,
+ /** tiled draw op list */
+ META_DRAW_TILE
+} MetaDrawType;
+
+typedef enum
+{
+ META_IMAGE_FILL_SCALE, /* default, needs to be all-bits-zero for g_new0 */
+ META_IMAGE_FILL_TILE
+} MetaImageFillType;
+
+struct _MetaDrawInfo
+{
+ GdkPixbuf *mini_icon;
+ GdkPixbuf *icon;
+ PangoLayout *title_layout;
+ int title_layout_width;
+ int title_layout_height;
+
+ MetaFrameBorders borders;
+ gint width;
+ gint height;
+};
+
+/**
+ * A single drawing operation in our simple vector drawing language.
+ */
+struct _MetaDrawOp
+{
+ MetaDrawType type;
+
+ /* Positions are strings because they can be expressions */
+ union
+ {
+ struct {
+ MetaColorSpec *color_spec;
+ int dash_on_length;
+ int dash_off_length;
+ int width;
+ MetaDrawSpec *x1;
+ MetaDrawSpec *y1;
+ MetaDrawSpec *x2;
+ MetaDrawSpec *y2;
+ } line;
+
+ struct {
+ MetaColorSpec *color_spec;
+ gboolean filled;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } rectangle;
+
+ struct {
+ MetaColorSpec *color_spec;
+ gboolean filled;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ double start_angle;
+ double extent_angle;
+ } arc;
+
+ struct {
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } clip;
+
+ struct {
+ MetaColorSpec *color_spec;
+ MetaAlphaGradientSpec *alpha_spec;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } tint;
+
+ struct {
+ MetaGradientSpec *gradient_spec;
+ MetaAlphaGradientSpec *alpha_spec;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } gradient;
+
+ struct {
+ MetaColorSpec *colorize_spec;
+ MetaAlphaGradientSpec *alpha_spec;
+ GdkPixbuf *pixbuf;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+
+ guint32 colorize_cache_pixel;
+ GdkPixbuf *colorize_cache_pixbuf;
+ MetaImageFillType fill_type;
+ unsigned int vertical_stripes : 1;
+ unsigned int horizontal_stripes : 1;
+ } image;
+
+ struct {
+ GtkStateFlags state;
+ GtkShadowType shadow;
+ GtkArrowType arrow;
+ gboolean filled;
+
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } gtk_arrow;
+
+ struct {
+ GtkStateFlags state;
+ GtkShadowType shadow;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } gtk_box;
+
+ struct {
+ GtkStateFlags state;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y1;
+ MetaDrawSpec *y2;
+ } gtk_vline;
+
+ struct {
+ MetaAlphaGradientSpec *alpha_spec;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ MetaImageFillType fill_type;
+ } icon;
+
+ struct {
+ MetaColorSpec *color_spec;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *ellipsize_width;
+ } title;
+
+ struct {
+ MetaDrawOpList *op_list;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ } op_list;
+
+ struct {
+ MetaDrawOpList *op_list;
+ MetaDrawSpec *x;
+ MetaDrawSpec *y;
+ MetaDrawSpec *width;
+ MetaDrawSpec *height;
+ MetaDrawSpec *tile_xoffset;
+ MetaDrawSpec *tile_yoffset;
+ MetaDrawSpec *tile_width;
+ MetaDrawSpec *tile_height;
+ } tile;
+
+ } data;
+};
+
+MetaDrawOp *meta_draw_op_new (MetaDrawType type);
+
+void meta_draw_op_free (MetaDrawOp *op);
+
+MetaDrawOpList *meta_draw_op_list_new (int n_preallocs);
+
+void meta_draw_op_list_ref (MetaDrawOpList *op_list);
+
+void meta_draw_op_list_unref (MetaDrawOpList *op_list);
+
+void meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list,
+ GtkStyleContext *context,
+ cairo_t *cr,
+ const MetaDrawInfo *info,
+ GdkRectangle rect);
+
+void meta_draw_op_list_append (MetaDrawOpList *op_list,
+ MetaDrawOp *op);
+
+gboolean meta_draw_op_list_validate (MetaDrawOpList *op_list,
+ GError **error);
+
+gboolean meta_draw_op_list_contains (MetaDrawOpList *op_list,
+ MetaDrawOpList *child);
+
+G_END_DECLS
+
+#endif