/*
Copyright (C) 2009-2015 Red Hat, 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.1 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, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "red-parse-qxl.h"
#include "display-channel.h"
#include "tree.h"
static const char *draw_type_to_str(uint8_t type)
{
switch (type) {
case QXL_DRAW_FILL:
return "QXL_DRAW_FILL";
case QXL_DRAW_OPAQUE:
return "QXL_DRAW_OPAQUE";
case QXL_DRAW_COPY:
return "QXL_DRAW_COPY";
case QXL_DRAW_TRANSPARENT:
return "QXL_DRAW_TRANSPARENT";
case QXL_DRAW_ALPHA_BLEND:
return "QXL_DRAW_ALPHA_BLEND";
case QXL_COPY_BITS:
return "QXL_COPY_BITS";
case QXL_DRAW_BLEND:
return "QXL_DRAW_BLEND";
case QXL_DRAW_BLACKNESS:
return "QXL_DRAW_BLACKNESS";
case QXL_DRAW_WHITENESS:
return "QXL_DRAW_WHITENESS";
case QXL_DRAW_INVERS:
return "QXL_DRAW_INVERS";
case QXL_DRAW_ROP3:
return "QXL_DRAW_ROP3";
case QXL_DRAW_COMPOSITE:
return "QXL_DRAW_COMPOSITE";
case QXL_DRAW_STROKE:
return "QXL_DRAW_STROKE";
case QXL_DRAW_TEXT:
return "QXL_DRAW_TEXT";
default:
return "?";
}
}
static void show_red_drawable(RedDrawable *drawable, const char *prefix)
{
if (prefix) {
printf("%s: ", prefix);
}
printf("%s effect %d bbox(%d %d %d %d)",
draw_type_to_str(drawable->type),
drawable->effect,
drawable->bbox.top,
drawable->bbox.left,
drawable->bbox.bottom,
drawable->bbox.right);
switch (drawable->type) {
case QXL_DRAW_FILL:
case QXL_DRAW_OPAQUE:
case QXL_DRAW_COPY:
case QXL_DRAW_TRANSPARENT:
case QXL_DRAW_ALPHA_BLEND:
case QXL_COPY_BITS:
case QXL_DRAW_BLEND:
case QXL_DRAW_BLACKNESS:
case QXL_DRAW_WHITENESS:
case QXL_DRAW_INVERS:
case QXL_DRAW_ROP3:
case QXL_DRAW_COMPOSITE:
case QXL_DRAW_STROKE:
case QXL_DRAW_TEXT:
break;
default:
spice_error("bad drawable type");
}
printf("\n");
}
static void show_draw_item(DrawItem *draw_item, const char *prefix)
{
if (prefix) {
printf("%s: ", prefix);
}
printf("effect %d bbox(%d %d %d %d)\n",
draw_item->effect,
draw_item->base.rgn.extents.x1,
draw_item->base.rgn.extents.y1,
draw_item->base.rgn.extents.x2,
draw_item->base.rgn.extents.y2);
}
typedef struct DumpItem {
int level;
Container *container;
} DumpItem;
static void dump_item(TreeItem *item, void *data)
{
DumpItem *di = data;
const char *item_prefix = "|--";
int i;
if (di->container) {
while (di->container != item->container) {
di->level--;
di->container = di->container->base.container;
}
}
switch (item->type) {
case TREE_ITEM_TYPE_DRAWABLE: {
Drawable *drawable = SPICE_CONTAINEROF(item, Drawable, tree_item.base);
const int max_indent = 200;
char indent_str[max_indent + 1];
int indent_str_len;
for (i = 0; i < di->level; i++) {
printf(" ");
}
printf(item_prefix, 0);
show_red_drawable(drawable->red_drawable, NULL);
for (i = 0; i < di->level; i++) {
printf(" ");
}
printf("| ");
show_draw_item(&drawable->tree_item, NULL);
indent_str_len = MIN(max_indent, strlen(item_prefix) + di->level * 2);
memset(indent_str, ' ', indent_str_len);
indent_str[indent_str_len] = 0;
region_dump(&item->rgn, indent_str);
printf("\n");
break;
}
case TREE_ITEM_TYPE_CONTAINER:
di->level++;
di->container = (Container *)item;
break;
case TREE_ITEM_TYPE_SHADOW:
break;
}
}
static void tree_foreach(TreeItem *item, void (*f)(TreeItem *, void *), void * data)
{
if (!item)
return;
f(item, data);
if (item->type == TREE_ITEM_TYPE_CONTAINER) {
Container *container = (Container*)item;
RingItem *it;
RING_FOREACH(it, &container->items) {
tree_foreach(SPICE_CONTAINEROF(it, TreeItem, siblings_link), f, data);
}
}
}
void tree_item_dump(TreeItem *item)
{
DumpItem di = { 0, };
spice_return_if_fail(item != NULL);
tree_foreach(item, dump_item, &di);
}
Shadow* shadow_new(DrawItem *item, const SpicePoint *delta)
{
spice_return_val_if_fail(item->shadow == NULL, NULL);
if (!delta->x && !delta->y) {
return NULL;
}
Shadow *shadow = spice_new(Shadow, 1);
shadow->base.type = TREE_ITEM_TYPE_SHADOW;
shadow->base.container = NULL;
shadow->owner = item;
region_clone(&shadow->base.rgn, &item->base.rgn);
region_offset(&shadow->base.rgn, delta->x, delta->y);
ring_item_init(&shadow->base.siblings_link);
region_init(&shadow->on_hold);
item->shadow = shadow;
return shadow;
}
Container* container_new(DrawItem *item)
{
Container *container = spice_new(Container, 1);
container->base.type = TREE_ITEM_TYPE_CONTAINER;
container->base.container = item->base.container;
item->base.container = container;
item->container_root = TRUE;
region_clone(&container->base.rgn, &item->base.rgn);
ring_item_init(&container->base.siblings_link);
ring_add_after(&container->base.siblings_link, &item->base.siblings_link);
ring_remove(&item->base.siblings_link);
ring_init(&container->items);
ring_add(&container->items, &item->base.siblings_link);
return container;
}
void container_free(Container *container)
{
spice_return_if_fail(ring_is_empty(&container->items));
ring_remove(&container->base.siblings_link);
region_destroy(&container->base.rgn);
free(container);
}
void container_cleanup(Container *container)
{
/* visit upward, removing containers */
/* non-empty container get its element moving up ?? */
while (container && container->items.next == container->items.prev) {
Container *next = container->base.container;
if (container->items.next != &container->items) {
TreeItem *item = (TreeItem *)ring_get_head(&container->items);
spice_assert(item);
ring_remove(&item->siblings_link);
ring_add_after(&item->siblings_link, &container->base.siblings_link);
item->container = container->base.container;
}
container_free(container);
container = next;
}
}
/* FIXME: document weird function: go down containers, and return drawable->shadow? */
Shadow* tree_item_find_shadow(TreeItem *item)
{
while (item->type == TREE_ITEM_TYPE_CONTAINER) {
if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)->items))) {
return NULL;
}
}
if (item->type != TREE_ITEM_TYPE_DRAWABLE) {
return NULL;
}
return ((DrawItem *)item)->shadow;
}
Ring *tree_item_container_items(TreeItem *item, Ring *ring)
{
return (item->container) ? &item->container->items : ring;
}
int tree_item_contained_by(TreeItem *item, Ring *ring)
{
spice_assert(item && ring);
do {
Ring *now = tree_item_container_items(item, ring);
if (now == ring) {
return TRUE;
}
} while ((item = (TreeItem *)item->container));
return FALSE;
}
void draw_item_remove_shadow(DrawItem *item)
{
Shadow *shadow;
if (!item->shadow) {
return;
}
shadow = item->shadow;
item->shadow = NULL;
ring_remove(&shadow->base.siblings_link);
region_destroy(&shadow->base.rgn);
region_destroy(&shadow->on_hold);
free(shadow);
}