/* * Copyright notice... */ #include "ctwm.h" #include #include #include #include #include "r_area_list.h" #include "r_area.h" /* * Prototype internal funcs */ static RAreaList *RAreaListCopy(const RAreaList *self); static void RAreaListDelete(RAreaList *self, int index); static void RAreaListAddList(RAreaList *self, const RAreaList *other); static RAreaList *RAreaListIntersectCrop(const RAreaList *self, const RArea *area); /* Sorts and their internal comparison routines */ static int _cmpX(const void *av, const void *bv); static void RAreaListSortX(const RAreaList *self); static int _cmpY(const void *av, const void *bv); static void RAreaListSortY(const RAreaList *self); /** * Create an RAreaList from a set of RArea's. * \param cap Hint as to the number of RArea's being passed * \param ... Sequence of RArea * to put in it. Don't forget a trailing * NULL. */ RAreaList * RAreaListNew(int cap, ...) { va_list ap; RAreaList *list; RArea *area; list = malloc(sizeof(RAreaList)); if(list == NULL) { abort(); } list->len = 0; if(cap <= 0) { cap = 1; } list->cap = cap; list->areas = malloc(cap * sizeof(RArea)); if(list->areas == NULL) { abort(); } va_start(ap, cap); while((area = va_arg(ap, RArea *)) != NULL) { RAreaListAdd(list, area); } va_end(ap); return list; } /** * Create a copy of a given RAreaList. */ static RAreaList * RAreaListCopy(const RAreaList *self) { RAreaList *new = RAreaListNew(self->cap, NULL); RAreaListAddList(new, self); return new; } /** * Create a copy of an RAreaList with given amounts cropped off the * sides. This is used principally during startup, to handle the * BorderBottom/Top/Left/Right config params. */ RAreaList * RAreaListCopyCropped(const RAreaList *self, int left_margin, int right_margin, int top_margin, int bottom_margin) { if(left_margin > 0 || right_margin > 0 || top_margin > 0 || bottom_margin > 0) { // Start with a big spanning square RArea big_area = RAreaListBigArea(self); // Guard against negative margins if(left_margin < 0) { left_margin = 0; } if(right_margin < 0) { right_margin = 0; } if(top_margin < 0) { top_margin = 0; } if(bottom_margin < 0) { bottom_margin = 0; } // Squeeze down the big square by the asked for amounts big_area.x += left_margin; big_area.width -= left_margin + right_margin; big_area.y += top_margin; big_area.height -= top_margin + bottom_margin; // If we cropped down to nothing, that's a RAreaList with nothing // in it, so give back that. if(big_area.width <= 0 || big_area.height <= 0) { return RAreaListNew(0, NULL); // empty list } // Make a new RAreaList cropped down to that size. return RAreaListIntersectCrop(self, &big_area); } // Nothing to do; our callers expect getting nothing back. return NULL; } /** * Clean up and free an RAreaList. */ void RAreaListFree(RAreaList *self) { if(self == NULL) { return; } free(self->areas); free(self); } /** * Delete an RArea from inside an RAreaList. */ static void RAreaListDelete(RAreaList *self, int index) { if(index >= self->len) { return; } self->len--; if(index == self->len) { return; } memcpy(&self->areas[index], &self->areas[index + 1], (self->len - index) * sizeof(RArea)); } /** * Add an RArea onto an RAreaList. */ void RAreaListAdd(RAreaList *self, const RArea *area) { if(self->cap == self->len) { RArea *new_list = realloc(self->areas, (self->cap + 1) * sizeof(RArea)); if(new_list == NULL) { abort(); } self->cap++; self->areas = new_list; } self->areas[self->len++] = *area; } /** * Add the RArea's from one RAreaList onto another. */ static void RAreaListAddList(RAreaList *self, const RAreaList *other) { if(self->cap - self->len < other->len) { RArea *new_list = realloc(self->areas, (self->len + other->len) * sizeof(RArea)); if(new_list == NULL) { abort(); } self->cap = self->len + other->len; self->areas = new_list; } memcpy(&self->areas[self->len], other->areas, other->len * sizeof(RArea)); self->len += other->len; } /** * qsort comparison function to sort by RArea.x */ static int _cmpX(const void *av, const void *bv) { const RArea *a = (const RArea *)av, *b = (const RArea *)bv; if(a->x < b->x) { return -1; } if(a->x > b->x) { return 1; } return (a->y > b->y) - (a->y < b->y); } /** * Sort the RArea's in an RAreaList by their x coordinate. */ static void RAreaListSortX(const RAreaList *self) { if(self->len <= 1) { return; } qsort(self->areas, self->len, sizeof(RArea), _cmpX); } /** * qsort comparison function to sort by RArea.t */ static int _cmpY(const void *av, const void *bv) { const RArea *a = (const RArea *)av, *b = (const RArea *)bv; if(a->y < b->y) { return -1; } if(a->y > b->y) { return 1; } return (a->x > b->x) - (a->x < b->x); } /** * Sort the RArea's in an RAreaList by their y coordinate. */ static void RAreaListSortY(const RAreaList *self) { if(self->len <= 1) { return; } qsort(self->areas, self->len, sizeof(RArea), _cmpY); } /** * Create an RAreaList whose RArea's are the horizontal union of our * RArea's. */ RAreaList * RAreaListHorizontalUnion(const RAreaList *self) { RAreaList *copy = RAreaListCopy(self); refine: // Two areas can't form a horizontal stripe if there's any space // between them. So start by putting them all in x-coord order to be // sure any gaps there are necessary. RAreaListSortX(copy); // Try HorizontalUnion'ing each area with the next one. If we can // create a union, replace them with it, and hop back to the top of // the process to start over. for(int i = 0; i < copy->len - 1; i++) { for(int j = i + 1; j < copy->len; j++) { RAreaList *repl = RAreaHorizontalUnion(©->areas[i], ©->areas[j]); if(repl != NULL) { if(repl->len) { RAreaListDelete(copy, j); RAreaListDelete(copy, i); RAreaListAddList(copy, repl); RAreaListFree(repl); goto refine; } RAreaListFree(repl); } } } return copy; } /** * Create an RAreaList whose RArea's are the vertical union of our * RArea's. */ RAreaList * RAreaListVerticalUnion(const RAreaList *self) { RAreaList *copy = RAreaListCopy(self); refine: // X-ref logic above in RAreaListHorizontalUnion() RAreaListSortY(copy); for(int i = 0; i < copy->len - 1; i++) { for(int j = i + 1; j < copy->len; j++) { RAreaList *repl = RAreaVerticalUnion(©->areas[i], ©->areas[j]); if(repl != NULL) { if(repl->len) { RAreaListDelete(copy, j); RAreaListDelete(copy, i); RAreaListAddList(copy, repl); RAreaListFree(repl); goto refine; } RAreaListFree(repl); } } } return copy; } /** * Create an RAreaList of all the areas in an RAreaList that a given * RArea intersects with. */ RAreaList * RAreaListIntersect(const RAreaList *self, const RArea *area) { RAreaList *new = RAreaListNew(self->len, NULL); for(int i = 0; i < self->len; i++) { if(RAreaIsIntersect(&self->areas[i], area)) { RAreaListAdd(new, &self->areas[i]); } } return new; } /** * Run a function over each RArea in an RAreaList until one returns true, * allowing them a place to stash other internal data. */ void RAreaListForeach(const RAreaList *self, bool (*func)(const RArea *cur_area, void *data), void *data) { for(int i = 0 ; i < self->len ; i++) { if(func(&(self->areas[i]), data) == true) { break; } } } /** * Create an RAreaList from another, cropped to a certain area defined by * an RArea. */ static RAreaList * RAreaListIntersectCrop(const RAreaList *self, const RArea *area) { RAreaList *new = RAreaListNew(self->len, NULL); for(int i = 0; i < self->len; i++) { RArea it = RAreaIntersect(&self->areas[i], area); if(RAreaIsValid(&it)) { RAreaListAdd(new, &it); } } return new; } /** * Create a maximal RArea describing the union of an RAreaList. * * This is used to construct a giant square that contains all our * monitors (and the dead area necessary to cover them). It winds up * being the equivalent of a spanning pseudo-Root window, and is used * when we need to figure some sort of "overall" positioning, like when * figuring "real" x/y coordinates. */ RArea RAreaListBigArea(const RAreaList *self) { int x, y, x2, y2; // Guard; probably impossible if(self->len < 1) { return RAreaInvalid(); } for(int i = 0 ; i < self->len ; i++) { const RArea *area = &(self->areas[i]); if(i == 0 || area->x < x) { x = area->x; } if(i == 0 || area->y < y) { y = area->y; } if(i == 0 || RAreaX2(area) > x2) { x2 = RAreaX2(area); } if(i == 0 || RAreaY2(area) > y2) { y2 = RAreaY2(area); } } return RAreaNew(x, y, x2 - x + 1, y2 - y + 1); } /** * Find the RArea in an RAreaList that has the largest intersection with * a given RArea. Colloquially, which area in an RAreaList does our * RArea mostly fit into? This is used to resize a window to fill one * monitor, by finding which monitor it's on. */ RArea RAreaListBestTarget(const RAreaList *self, const RArea *area) { RArea full_area = RAreaInvalid(); int max_area = -1; for(int i = 0; i < self->len; i++) { RArea it = RAreaIntersect(area, &self->areas[i]); if(RAreaIsValid(&it) && (max_area < 0 || RAreaArea(&it) > max_area)) { max_area = RAreaArea(&it); full_area = self->areas[i]; } } return full_area; } /** * Find the x coordinate of the right-most RArea in an RAreaList. */ int RAreaListMaxX(const RAreaList *self) { RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len]; int max_x = self->len ? cur_area->x : 0; // While a for(i=0 ; ilen ; i++) loop is generally nicer for // these iterations, it winds up being a little trickier here, so we // leave it as a pointer-stepper. while(++cur_area < area_end) { if(cur_area->x > max_x) { max_x = cur_area->x; } } return max_x; } /** * Find the y coordinate of the bottom-most RArea in an RAreaList. */ int RAreaListMaxY(const RAreaList *self) { RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len]; int max_y = self->len ? cur_area->y : 0; while(++cur_area < area_end) { if(cur_area->y > max_y) { max_y = cur_area->y; } } return max_y; } /** * Find the x coordinate of the right edge of the left-most RArea in an * RAreaList. */ int RAreaListMinX2(const RAreaList *self) { RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len]; int min_x = self->len ? RAreaX2(cur_area) : 0; while(++cur_area < area_end) { if(RAreaX2(cur_area) < min_x) { min_x = RAreaX2(cur_area); } } return min_x; } /** * Find the y coordinate of the bottom edge of the top-most RArea in an * RAreaList. */ int RAreaListMinY2(const RAreaList *self) { RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len]; int min_y = self->len ? RAreaY2(cur_area) : 0; while(++cur_area < area_end) { if(RAreaY2(cur_area) < min_y) { min_y = RAreaY2(cur_area); } } return min_y; } /** * Pretty-print an RAreaList. * * Used for dev/debug. */ void RAreaListPrint(const RAreaList *self) { fprintf(stderr, "[len=%d cap=%d", self->len, self->cap); for(int i = 0 ; i < self->len ; i++) { RArea *area = &self->areas[i]; fprintf(stderr, " "); RAreaPrint(area); } fprintf(stderr, "]"); }