/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* * cl_layer.c - API for the CL_Layer type */ #include "prtypes.h" #include "xp_mem.h" /* For XP_NEW_ZAP */ #include "xpassert.h" /* For XP_ASSERT, et al */ #include "xp_list.h" #include "layers.h" #include "cl_priv.h" #ifdef XP_MAC #include "macstdlibextras.h" /* for strdup */ #endif static void cl_hidden_changed(CL_Layer *layer); /* Allocate and initialize a new layer */ CL_Layer * CL_NewLayer(char *name, int32 x_offset, int32 y_offset, XP_Rect *bbox, CL_LayerVTable *vtable, uint32 flags, void *client_data) { CL_Layer *new_layer; new_layer = XP_NEW_ZAP(CL_Layer); if (new_layer == NULL) return NULL; new_layer->name = name ? strdup(name) : NULL; if (bbox) new_layer->bbox = *bbox; /* XXX - consider sharing the vtable, instead of copying it */ if (vtable) new_layer->vtable = *vtable; new_layer->x_offset = x_offset; new_layer->y_offset = y_offset; new_layer->hidden = !!(flags & CL_HIDDEN); new_layer->scrolling = !!(flags & CL_DONT_SCROLL_WITH_DOCUMENT); new_layer->clip_children = !!(flags & CL_CLIP_CHILD_LAYERS); new_layer->clip_self = !(flags & CL_DONT_CLIP_SELF); new_layer->opaque = !!(flags & CL_OPAQUE); new_layer->prefer_draw_onscreen = !!(flags & CL_PREFER_DRAW_ONSCREEN); new_layer->prefer_draw_offscreen = !!(flags & CL_PREFER_DRAW_OFFSCREEN); new_layer->enumerated = !(flags & CL_DONT_ENUMERATE); new_layer->cutout = !!(flags & CL_CUTOUT); new_layer->override_inherit_visibility = !!(flags & CL_OVERRIDE_INHERIT_VISIBILITY); new_layer->client_data = client_data; new_layer->draw_region = FE_NULL_REGION; return new_layer; } /* Destroy an existing layer */ void CL_DestroyLayer(CL_Layer *layer) { CL_Layer *parent_layer; XP_ASSERT(layer); if (!layer) return; if (layer->compositor) cl_LayerDestroyed(layer->compositor, layer); parent_layer = CL_GetLayerParent(layer); if (parent_layer) CL_RemoveChild(parent_layer, layer); if (layer->vtable.destroy_func) (*layer->vtable.destroy_func)(layer); XP_FREEIF(layer->uniform_color); XP_FREEIF(layer->name); XP_FREE(layer); } /* Frees an entire sub-tree, rooted at the specified node */ void CL_DestroyLayerTree(CL_Layer *root) { CL_Layer *child, *next; XP_ASSERT(root); if (!root) return; /* Iterate through the child list. For each child, recurse and free */ for (child = root->top_child; child; child = next) { /* Need to grab the next child here, because child will be freed */ next = child->sib_below; CL_DestroyLayerTree(child); } CL_DestroyLayer(root); } uint32 CL_GetLayerFlags(CL_Layer *layer) { uint32 flags = 0; flags |= CL_HIDDEN * (layer->hidden == PR_TRUE); flags |= CL_DONT_SCROLL_WITH_DOCUMENT * (layer->scrolling == PR_FALSE); flags |= CL_CLIP_CHILD_LAYERS * (layer->clip_children == PR_TRUE); flags |= CL_DONT_CLIP_SELF * (layer->clip_self == PR_FALSE); flags |= CL_OPAQUE * (layer->opaque == PR_TRUE); flags |= CL_PREFER_DRAW_ONSCREEN * (layer->prefer_draw_onscreen == PR_TRUE); flags |= CL_PREFER_DRAW_OFFSCREEN * (layer->prefer_draw_offscreen == PR_TRUE); flags |= CL_DONT_ENUMERATE * (layer->enumerated == PR_FALSE); flags |= CL_CUTOUT * (layer->cutout == PR_TRUE); flags |= CL_OVERRIDE_INHERIT_VISIBILITY * (layer->override_inherit_visibility == PR_TRUE); return flags; } static PRBool cl_change_layer_flags(CL_Layer *layer, uint32 new_flags) { PRBool success; uint32 old_flags = CL_GetLayerFlags(layer); uint32 changed_flags = old_flags ^ new_flags; /* Assume success */ success = PR_TRUE; if (changed_flags == 0) return success; if (changed_flags & CL_HIDDEN) CL_SetLayerHidden(layer, (PRBool)((new_flags & CL_HIDDEN) != 0)); if (changed_flags & CL_OVERRIDE_INHERIT_VISIBILITY) { layer->override_inherit_visibility = !!(new_flags & CL_OVERRIDE_INHERIT_VISIBILITY); cl_hidden_changed(layer); } if (changed_flags & (CL_DONT_SCROLL_WITH_DOCUMENT | CL_CLIP_CHILD_LAYERS | CL_DONT_CLIP_SELF)) { XP_ASSERT(0); /* We don't support changing this on the fly yet */ success = PR_FALSE; } if (changed_flags & CL_OPAQUE) { layer->opaque = ((new_flags & CL_OPAQUE) != 0); if (!layer->opaque) CL_SetLayerUniformColor(layer, NULL); } if (changed_flags & CL_PREFER_DRAW_ONSCREEN) { layer->prefer_draw_onscreen = (new_flags & CL_PREFER_DRAW_ONSCREEN) != 0; layer->prefer_draw_offscreen = PR_FALSE; } if (changed_flags & CL_PREFER_DRAW_OFFSCREEN) { layer->prefer_draw_offscreen = (new_flags & CL_PREFER_DRAW_OFFSCREEN) != 0; layer->prefer_draw_onscreen = PR_FALSE; } if (changed_flags & CL_DONT_ENUMERATE) layer->enumerated = (new_flags & CL_DONT_ENUMERATE) == 0; if (changed_flags & CL_CUTOUT) layer->cutout = (new_flags & CL_CUTOUT) != 0; return success; } extern PRBool CL_ChangeLayerFlag(CL_Layer *layer, uint32 flag, PRBool value) { uint32 old_flags, new_flags; LOCK_LAYERS(layer); old_flags = CL_GetLayerFlags(layer); if (value) new_flags = old_flags | flag; else new_flags = old_flags & ~flag; cl_change_layer_flags(layer, new_flags); UNLOCK_LAYERS(layer); return value; } PRBool CL_ForEachChildOfLayer(CL_Layer *parent, CL_ChildOfLayerFunc func, void *closure) { CL_Layer *child, *next; PRBool done; CL_Compositor *compositor; XP_ASSERT(parent); XP_ASSERT(func); if (!parent) return PR_FALSE; compositor = parent->compositor; if (compositor) LOCK_COMPOSITOR(compositor); /* Iterate through the child list. For each child, recurse and free */ for (child = parent->top_child; child; child = next) { /* * Need to grab the next child here, in case child * is no longer valid */ next = child->sib_below; if (func) { done = (PRBool) ! (*func)(child, closure); if (done) { if (compositor) UNLOCK_COMPOSITOR(compositor); return PR_FALSE; } } } if (compositor) UNLOCK_COMPOSITOR(compositor); return PR_TRUE; } void cl_SetCompositorRecursive(CL_Layer *layer, CL_Compositor *compositor) { CL_Layer *child; for(child = layer->top_child; child; child = child->sib_below) cl_SetCompositorRecursive(child, compositor); layer->compositor = compositor; } /* Insert a layer into the layer tree as a child of the parent layer. * If sibling is NULL, the layer is added as the topmost (in z-order) child * if where==CL_ABOVE or the bottommost child if where==CL_BELOW. * If sibling is non-NULL, the layer is added above or below (in z-order) * sibling based on the value of where. */ void CL_InsertChild(CL_Layer *parent, CL_Layer *child, CL_Layer *sibling, CL_LayerPosition where) { XP_ASSERT(parent); if (!parent) return; LOCK_LAYERS(parent); /* If no sibling is specified... */ if (sibling == NULL) { /* If there are no children */ if (parent->top_child == NULL) parent->top_child = parent->bottom_child = child; else if (where == CL_ABOVE) { /* Add it as the first child */ child->sib_below = parent->top_child; child->sib_above = NULL; parent->top_child = child; if (child->sib_below) child->sib_below->sib_above = child; child->z_index = child->sib_below->z_index; } else { /* Add it as the last child */ child->sib_above = parent->bottom_child; child->sib_below = NULL; parent->bottom_child = child; if (child->sib_above) child->sib_above->sib_below = child; child->z_index = child->sib_above->z_index; } } else { if (where == CL_ABOVE) { child->sib_above = sibling->sib_above; child->sib_below = sibling; /* If there is a sibling above this, point it at child */ if (sibling->sib_above) sibling->sib_above->sib_below = child; /* Otherwise, sibling was the first child */ else parent->top_child = child; sibling->sib_above = child; } else { child->sib_below = sibling->sib_below; child->sib_above = sibling; /* If there is a sibling below this, point it at child */ if (sibling->sib_below) sibling->sib_below->sib_above = child; /* Otherwise, sibling was the last child */ else parent->bottom_child = child; sibling->sib_below = child; } child->z_index = sibling->z_index; } cl_SetCompositorRecursive(child, parent->compositor); child->parent = parent; if (child->compositor) cl_LayerAdded(child->compositor, child); UNLOCK_LAYERS(parent); } void CL_InsertChildByZ(CL_Layer *parent, CL_Layer *child, int32 z_index) { CL_Layer *sibling; XP_ASSERT(parent); if (!parent) return; LOCK_LAYERS(parent); for (sibling = parent->top_child; sibling; sibling = sibling->sib_below) { if (sibling->z_index <= z_index) break; } if (sibling) CL_InsertChild(parent, child, sibling, CL_ABOVE); else CL_InsertChild(parent, child, NULL, CL_BELOW); child->z_index = z_index; UNLOCK_LAYERS(parent); } /* Removes a layer from a parent's sub-tree */ void CL_RemoveChild(CL_Layer *parent, CL_Layer *child) { XP_ASSERT(parent); XP_ASSERT(child); if (!parent || !child) return; LOCK_LAYERS(parent); if (child == parent->top_child) parent->top_child = child->sib_below; if (child == parent->bottom_child) parent->bottom_child = child->sib_above; if (child->sib_above) child->sib_above->sib_below = child->sib_below; if (child->sib_below) child->sib_below->sib_above = child->sib_above; if (CL_IsMouseEventGrabber(child->compositor, child)) CL_GrabMouseEvents(child->compositor, NULL); if (CL_IsKeyEventGrabber(child->compositor, child)) CL_GrabKeyEvents(child->compositor, NULL); child->parent = NULL; cl_LayerRemoved(parent->compositor, child); UNLOCK_LAYERS(parent); } /* A rectangle that covers the entire world. Note: We don't use the extrema values for 32-bit integers. Instead, we deliberately leave headroom to avoid overflow of the coordinates as a result of translating the rect. */ static XP_Rect max_rect = CL_MAX_RECT; /* Return the clipping box, in document coordinates, that this layer applies to child layers */ static XP_Rect * cl_clipping_bbox(CL_Layer *layer) { if (! layer) return &max_rect; if (layer->clip_children) return &layer->clipped_bbox; return cl_clipping_bbox(layer->parent); } /* Propagate changes in the bboxes of layers to their children. This may cause the origin of child layers to change and may also change their widths and heights due to clipping. */ static PRBool cl_bbox_changed_recurse(CL_Layer *layer, XP_Rect *parent_clip_bbox, PRBool ancestor_not_hidden, int32 x_parent_origin, int32 y_parent_origin) { CL_Layer *child; XP_Rect *child_clip_bbox; int32 x_origin, y_origin; PRBool visible, not_hidden; int descendant_visible; XP_Rect *clipped_bbox = &layer->clipped_bbox; XP_Rect *layer_bbox = &layer->bbox; PRBool clip_children = (PRBool)(layer->clip_children && layer->clip_self); /* Compute new layer origin, in document coordinates */ x_origin = layer->x_origin = x_parent_origin + layer->x_offset; y_origin = layer->y_origin = y_parent_origin + layer->y_offset; /* A layer is considered visible for purposes of compositing if ... = the layer is not hidden, and = none of the layer's ancestors is hidden, and = the layer is not entirely clipped by ancestor layers */ not_hidden = visible = (PRBool)(!layer->hidden && (ancestor_not_hidden || layer->override_inherit_visibility)); if (layer->clip_self) { /* Get layer bounds in coordinates relative to parent layer */ XP_CopyRect(layer_bbox, clipped_bbox); /* Convert to document coordinates */ XP_OffsetRect(clipped_bbox, x_origin, y_origin); /* Clip by parents bbox */ if (parent_clip_bbox) { if (layer->cutout) { /* Special case: a cutout layer is rendered invisibly if it is clipped, even partially, by an ancestor. */ visible &= XP_RectContainsRect(parent_clip_bbox, clipped_bbox); XP_IntersectRect(clipped_bbox, parent_clip_bbox, clipped_bbox); } else { XP_IntersectRect(clipped_bbox, parent_clip_bbox, clipped_bbox); /* "Normal" layers are only invisible if they're wholly clipped by ancestors. */ visible &= !XP_IsEmptyRect(clipped_bbox); } } } else { XP_CopyRect(parent_clip_bbox, clipped_bbox); } layer->visible = visible; child_clip_bbox = clip_children ? clipped_bbox : parent_clip_bbox; descendant_visible = 0; for (child = layer->top_child; child; child = child->sib_below) { descendant_visible |= (int)cl_bbox_changed_recurse(child, child_clip_bbox, not_hidden, layer->x_origin, layer->y_origin); } layer->visible = visible; layer->descendant_visible = (PRBool)descendant_visible; return visible; } static void cl_property_changed(CL_Layer *layer) { int32 x_offset, y_offset; XP_Rect *child_clip_bbox; PRBool ancestor_not_hidden; CL_Layer *parent, *layer2; if (layer->compositor) { layer->compositor->recompute_update_region = PR_TRUE; cl_start_compositor_timeouts(layer->compositor); } parent = layer->parent; if (parent) { layer2 = parent; ancestor_not_hidden = PR_TRUE; while (layer2) { if (layer2->hidden) { ancestor_not_hidden = PR_FALSE; break; } layer2 = layer2->parent; } child_clip_bbox = cl_clipping_bbox(parent); x_offset = parent->x_origin; y_offset = parent->y_origin; } else { ancestor_not_hidden = PR_TRUE; child_clip_bbox = &max_rect; x_offset = 0; y_offset = 0; } /* If a layer is visible for compositing, then so are all its ancestors */ if (cl_bbox_changed_recurse(layer, child_clip_bbox, ancestor_not_hidden, x_offset, y_offset)) while (parent) { parent->descendant_visible = PR_TRUE; parent = parent->parent; } } static void cl_bbox_changed(CL_Layer *layer) { cl_property_changed(layer); } void cl_ParentChanged(CL_Layer *layer) { cl_property_changed(layer); } static void cl_hidden_changed(CL_Layer *layer) { cl_property_changed(layer); } static void cl_position_changed(CL_Layer *layer) { cl_property_changed(layer); } static void cl_set_hidden (CL_Layer *layer, PRBool hidden) { if (hidden == layer->hidden) return; layer->hidden = hidden; /* If the layer is currently visible and it's to be hidden, or the layer isn't visible and it's being un-hidden, update the area occupied by the layer. */ if (layer->visible == hidden) cl_hidden_changed(layer); } /* Change the physical position of a layer with relative values */ /* May trigger asynchronous drawing to update screen */ void CL_OffsetLayer(CL_Layer *layer, int32 x_offset, int32 y_offset) { XP_ASSERT(layer); if (!layer) return; CL_MoveLayer(layer, layer->x_offset + x_offset, layer->y_offset + y_offset); } /* Change the position of a layer. XY coordinates are relative to the origin of the parent layer. May trigger asynchronous drawing to update screen */ void CL_MoveLayer(CL_Layer *layer, int32 x, int32 y) { XP_ASSERT(layer); if (!layer) return; LOCK_LAYERS(layer); /* Layer position changed ? */ if ((x == layer->x_offset) && (y == layer->y_offset)) { UNLOCK_LAYERS(layer); return; } layer->x_offset = x; layer->y_offset = y; cl_position_changed(layer); cl_LayerMoved(layer->compositor, layer); UNLOCK_LAYERS(layer); } /* * Change the dimensions of a layer * May trigger asynchronous drawing to update screen * FIXME - There is a performance bug here that shows up when a * layer's width grows, but its height shrinks or vice-versa. * The update rectangles are overdrawn for these cases. */ void CL_ResizeLayer(CL_Layer *layer, int32 width, int32 height) { XP_Rect *layer_bbox; int32 old_width, old_height; XP_ASSERT(layer); if (!layer) /* Paranoia */ return; LOCK_LAYERS(layer); /* Get the existing layer width and height */ layer_bbox = &layer->bbox; old_width = layer_bbox->right - layer_bbox->left; old_height = layer_bbox->bottom - layer_bbox->top; if ((width == old_width) && (height == old_height)) { UNLOCK_LAYERS(layer); return; } /* Reset layer bbox to new size */ layer_bbox->right = layer_bbox->left + width; layer_bbox->bottom = layer_bbox->top + height; cl_bbox_changed(layer); UNLOCK_LAYERS(layer); } int32 CL_GetLayerXOffset(CL_Layer *layer) { return layer->x_offset; } int32 CL_GetLayerYOffset(CL_Layer *layer) { return layer->y_offset; } int32 CL_GetLayerXOrigin(CL_Layer *layer) { return layer->x_origin; } int32 CL_GetLayerYOrigin(CL_Layer *layer) { return layer->y_origin; } void CL_SetLayerUniformColor(CL_Layer *layer, CL_Color *color) { if (color) { CL_Color *copy_color = XP_NEW(CL_Color); XP_ASSERT(copy_color); if (!copy_color) return; *copy_color = *color; layer->uniform_color = copy_color; } else { XP_FREEIF(layer->uniform_color); } } void CL_SetLayerOpaque(CL_Layer *layer, PRBool opaque) { layer->opaque = opaque; } PRBool CL_GetLayerOpaque(CL_Layer *layer) { return layer->opaque; } int32 CL_GetLayerZIndex(CL_Layer *layer) { return layer->z_index; } /* Change the bounding box of a layer */ void CL_SetLayerBbox(CL_Layer *layer, XP_Rect *new_bbox) { XP_ASSERT(layer); if (!layer) return; LOCK_LAYERS(layer); /* Don't do anything if the bbox didn't change. */ if (XP_EqualRect(new_bbox, &layer->bbox)) { UNLOCK_LAYERS(layer); return; } layer->bbox = *new_bbox; cl_sanitize_bbox(&layer->bbox); cl_bbox_changed(layer); UNLOCK_LAYERS(layer); } void CL_GetLayerBbox(CL_Layer *layer, XP_Rect *bbox) { XP_ASSERT(layer); XP_ASSERT(bbox); if (!layer || !bbox) return; LOCK_LAYERS(layer); bbox->left = layer->bbox.left; bbox->top = layer->bbox.top; bbox->right = layer->bbox.right; bbox->bottom = layer->bbox.bottom; UNLOCK_LAYERS(layer); } void CL_GetLayerBboxAbsolute(CL_Layer *layer, XP_Rect *bbox) { XP_ASSERT(layer); XP_ASSERT(bbox); if (!layer || !bbox) return; LOCK_LAYERS(layer); bbox->left = layer->x_origin; bbox->top = layer->y_origin; bbox->right = layer->x_origin + (layer->bbox.right - layer->bbox.left); bbox->bottom = layer->y_origin + (layer->bbox.bottom - layer->bbox.top); UNLOCK_LAYERS(layer); } char * CL_GetLayerName(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->name; } void CL_SetLayerName(CL_Layer *layer, char *name) { XP_ASSERT(layer); if (!layer) return; layer->name = name; } PRBool CL_GetLayerHidden(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return PR_FALSE; else return layer->hidden; } void CL_SetLayerHidden(CL_Layer *layer, PRBool hidden) { XP_ASSERT(layer); if (!layer) return; cl_set_hidden(layer, hidden); } void * CL_GetLayerClientData(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->client_data; } void CL_SetLayerClientData(CL_Layer *layer, void *client_data) { XP_ASSERT(layer); if (!layer) return; layer->client_data = client_data; } CL_Compositor * CL_GetLayerCompositor(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->compositor; } CL_Layer * CL_GetLayerSiblingAbove(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->sib_above; } CL_Layer * CL_GetLayerSiblingBelow(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->sib_below; } static CL_Layer * cl_bottommost(CL_Layer *layer) { while (layer->bottom_child && layer->bottom_child->z_index < 0) layer = layer->bottom_child; return layer; } static CL_Layer * cl_GetLayerAbove2(CL_Layer *layer, PRBool consider_children) { CL_Layer *layer_above; if (! layer) return NULL; if (consider_children && layer->top_child && (layer->top_child->z_index >= 0)) { CL_Layer *child; child = layer->bottom_child; while (child->z_index < 0) child = child->sib_above; layer_above = child; } else if (layer->z_index < 0) { if (layer->sib_above && (layer->sib_above->z_index < 0)) layer_above = cl_bottommost(layer->sib_above); else layer_above = layer->parent; } else { if (layer->sib_above) layer_above = cl_bottommost(layer->sib_above); else layer_above = cl_GetLayerAbove2(layer->parent, PR_FALSE); } if (!layer_above) return NULL; if (!layer_above->enumerated) return cl_GetLayerAbove2(layer_above, PR_TRUE); return layer_above; } CL_Layer * CL_GetLayerAbove(CL_Layer *layer) { CL_Layer *layer_above; XP_ASSERT(layer); LOCK_LAYERS(layer); layer_above = cl_GetLayerAbove2(layer, PR_TRUE); UNLOCK_LAYERS(layer); return layer_above; } static CL_Layer * cl_topmost(CL_Layer *layer) { while (layer->top_child && layer->top_child->z_index >= 0) layer = layer->top_child; return layer; } static CL_Layer * cl_GetLayerBelow2(CL_Layer *layer, PRBool consider_children) { CL_Layer *layer_below; if (! layer) return NULL; if (consider_children && layer->bottom_child && (layer->bottom_child->z_index < 0)) { CL_Layer *child; child = layer->top_child; while (child->z_index >= 0) child = child->sib_below; layer_below = child; } else if (layer->z_index >= 0) { if (layer->sib_below && (layer->sib_below->z_index >= 0)) layer_below = cl_topmost(layer->sib_below); else layer_below = layer->parent; } else { if (layer->sib_below) layer_below = cl_topmost(layer->sib_below); else layer_below = cl_GetLayerBelow2(layer->parent, PR_FALSE); } if (!layer_below) return NULL; if (!layer_below->enumerated) return cl_GetLayerBelow2(layer_below, PR_TRUE); return layer_below; } CL_Layer * CL_GetLayerBelow(CL_Layer *layer) { CL_Layer *layer_below; XP_ASSERT(layer); LOCK_LAYERS(layer); layer_below = cl_GetLayerBelow2(layer, PR_TRUE); UNLOCK_LAYERS(layer); return layer_below; } CL_Layer * CL_GetLayerChildByName(CL_Layer *layer, char *name) { CL_Layer *child; XP_ASSERT(layer); if (!layer) return NULL; for (child = layer->top_child; child; child = child->sib_below) if (child->name && strcmp(child->name, name) == 0) return child; return NULL; } CL_Layer * CL_GetLayerChildByIndex(CL_Layer *layer, uint index) { CL_Layer *child; XP_ASSERT(layer); if (!layer) return NULL; LOCK_LAYERS(layer); for (child = layer->bottom_child; child && index; child = child->sib_above) { if (child->enumerated) index--; } while (child && !child->enumerated) child = child->sib_above; UNLOCK_LAYERS(layer); return child; } int CL_GetLayerChildCount(CL_Layer *layer) { CL_Layer *child; int child_count = 0; if (!layer) return 0; XP_ASSERT(layer); LOCK_LAYERS(layer); for (child = layer->top_child; child; child = child->sib_below) { if (child->enumerated) child_count++; } UNLOCK_LAYERS(layer); return child_count; } void CL_GetLayerVTable(CL_Layer *layer, CL_LayerVTable *vtable) { XP_ASSERT(layer); XP_ASSERT(vtable); if (!layer || !vtable) return; *vtable = layer->vtable; } void CL_SetLayerVTable(CL_Layer *layer, CL_LayerVTable *vtable) { XP_ASSERT(layer); if (!layer) return; if (vtable) layer->vtable = *vtable; else { layer->vtable.painter_func = NULL; layer->vtable.region_cleanup_func = NULL; layer->vtable.event_handler_func = NULL; layer->vtable.destroy_func = NULL; } } CL_Layer *CL_GetLayerParent(CL_Layer *layer) { XP_ASSERT(layer); if (!layer) return NULL; else return layer->parent; }