#include #include #include "dims.h" #include "node.h" #include "surface.h" /* Internal allocator method for ct_node structures. */ static inline struct ct_node *__alloc_node(void) { struct ct_node *node = (struct ct_node *)malloc(sizeof(struct ct_node)); return node; } /* Returns NULL if memory allocation failed. * TODO: should dims be given as a parameter, or lazily computed in ct_update? */ struct ct_node *new_node(struct ct_dims *const dims, struct ct_bounds *const bounds, struct ct_node *const parent) { struct ct_node *node = __alloc_node(); if (node != NULL) { *node = (struct ct_node){ /* copy the parent's dimensions for now and request * cursetree resize this node appropriately afterwards * WARNING: new_node doesn't set the NFLAG_RESIZE request flag * WARNING: that should be done by a function calling new_node */ .surface = new_surface(parent->surface->dims, bounds), .flags = NFLAG_EMPTY, .parent = parent, .child = (struct ct_node **)malloc(NODE_INIT_CHILDREN * sizeof(struct ct_node *)), .csize = NODE_INIT_CHILDREN, .cindex = 0, .axis = AXIS_X, }; } return node; } /* WARNING: Do NOT use __destroy_node() to destroy a node's children! * WARNING: Use the destroy_child_node() function instead. */ void __destroy_node(struct ct_node *const node) { if (node == NULL) return; // destroy children first for (int j = 0; j < node->cindex; j++) { __destroy_node(node->child[j]); } destroy_surface(node->surface); free(node); } void satisfy_node(struct ct_node *const node, struct ct_dims *const dims) { double axismax; if (IS_PARENT_NODE(node)) { for (int i = 0; i < node->cindex; i++) { if (node->axis == AXIS_X) { axismax += dims->width * node->child[i]->surface->bounds->wmax; } else { assert(node->axis == AXIS_Y); axismax += dims->width * node->child[i]->surface->bounds->hmax; } } } } /* */ void resize_node(struct ct_node *const node, struct ct_dims *const dims) { int cwidth, cheight; // child dimensions if (IS_PARENT_NODE(node)) { dims->width / node->cindex; } resize_surface(node->surface, dims); for (int j = 0; j < node->cindex; j++) { /* TODO */ resize_node(node->child[j], TODO); } // bifurcate_dims(dims, node->axis, node->ratio, &dims0, &dims1); // resize_node(node->child[0], dims0); // resize_node(node->child[1], dims1); } static int __set_cbounds(struct ct_node *const parent, const struct ct_node *const child, const bool additive) { /* child and parent w/h min aliases */ int c_wmin, c_hmin; int p_wmin_rel, p_hmin_rel; c_wmin = child->surface->bounds->wmin; c_hmin = child->surface->bounds->hmin; if (!additive) { c_wmin *= -1; c_hmin *= -1; } if (child->surface->bounds->type == BOUND_RELATIVE) { parent->cbounds.wmin_abs++; parent->cbounds.hmin_abs++; p_wmin_rel = parent->cbounds.wmin_rel + c_wmin; p_hmin_rel = parent->cbounds.hmin_rel + c_wmin; if (p_wmin_rel > __BOUND_REL_MAX || p_hmin_rel >= __BOUND_REL_MAX) return 1; parent->cbounds.wmin_rel = p_wmin_rel; parent->cbounds.hmin_rel = p_hmin_rel; assert(parent->cbounds.wmin_rel > __BOUND_REL_MIN); assert(parent->cbounds.hmin_rel > __BOUND_REL_MIN); } else { parent->cbounds.wmin_abs += c_wmin; parent->cbounds.hmin_abs += c_hmin; assert(parent->cbounds.wmin_abs > __BOUND_ABS_MIN); assert(parent->cbounds.hmin_abs > __BOUND_ABS_MIN); } return 0; } /* Returns: * 0 -> success * 1 -> failed (max child limit reached) * 2 -> failed (cumulative relative bounds surpasses 100%) */ int insert_child_node(struct ct_node *const parent, struct ct_node *const child, const cindex i) { if (parent->cindex == CINDEX_MAX) return 1; else if (__set_cbounds(parent, child, true)) return 2; else if (parent->cindex == parent->csize) { // grow child array size and clamp maximum parent->csize *= NODE_CHILDREN_GROWTH; if (parent->csize > CINDEX_MAX) parent->csize = CINDEX_MAX; parent->child = reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); } // shift all children up for insertion for (int j = parent->cindex; j > i; j--) { parent->child[j] = parent->child[j - 1]; } // do insertion parent->child[i] = child; parent->cindex++; // request cursetree recompute dimensions recursively from parent parent->flags |= NFLAG_RESIZE; return EXIT_SUCCESS; } /* Returns: * 0 -> success * 1 -> failed (max child limit reached) * 2 -> failed (cumulative relative bounds surpasses 100%) */ int append_child_node(struct ct_node *const parent, struct ct_node *const child) { return insert_child_node(parent, child, parent->cindex); } /* Remove a child node from a parent node by index. * Returns NULL if an invalid index was provided, otherwise returns * a pointer to the child node removed. * NOTE: This does NOT destroy the child node and should be * NOTE: used when moving the child somewhere else. * NOTE: Otherwise use destroy_child_node() instead. */ struct ct_node *remove_child_node(struct ct_node *const parent, const cindex i) { struct ct_node *child; if (i >= parent->cindex) return NULL; else if (parent->cindex <= parent->csize / NODE_CHILDREN_GROWTH) { // shrink child array to avoid memory bloat parent->csize /= NODE_CHILDREN_GROWTH; parent->child = reallocarray(parent->child, parent->csize, sizeof(struct ct_node *)); } child = &parent[i]; // shift all children down to fill removal for (int j = i; j < parent->cindex; j++) { parent->child[j] = parent->child[j + 1]; } parent->cindex--; __set_cbounds(parent, child, false); // request cursetree recompute dimensions recursively from parent parent->flags |= NFLAG_RESIZE; return child; } /* Remove and destroy a child node by index. * Returns 1 on failure (invalid index provided), and 0 on success. * NOTE: Use remove_child_node() instead if the child should live. */ int destroy_child_node(struct ct_node *const parent, const cindex i) { struct ct_node *child = remove_child_node(parent, i); __destroy_node(child); return (child == NULL); } /* * Returns: * 0 -> success * 1 -> failed (node has no parent) * 2 -> failed (parent has no reference to node, CRITICAL) */ static int __index_as_child(const struct ct_node *const node, int *const index) { if (node->parent == NULL) return 1; for (int i = 0; i < node->parent->cindex; i++) { if (node->parent->child[i] == node) { *index = i; return 0; } } return 2; } /* * If preserve_bounds is set then upon collapse the new node maintains the * original node's bounds struct. Otherwise the bounds of the child collapsed * onto are used. */ void collapse_node(struct ct_node **const node, const int i, const bool preserve_bounds) { assert(0 <= i && i < (*node)->cindex); int parent_index; struct ct_node *const parent = (*node)->parent; struct ct_node *collapse_target = remove_child_node(*node, i); struct ct_dims *dims = dup_dims((*node)->surface->dims); struct ct_bounds *bounds; if (preserve_bounds) { bounds = dup_bounds((*node)->surface->bounds); rebind_surface(collapse_target->surface, bounds); } /* Destroy original node and point to the collapse_target */ if (__index_as_child(*node, &parent_index)) { /* __index_as_child fails (typically) because * the *node is cursetree's root node. */ __destroy_node(*node); *node = collapse_target; } else { destroy_child_node(parent, parent_index); insert_child_node(parent, collapse_target, parent_index); } resize_node(collapse_target, dims); }