Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright notice...
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <stdarg.h>
     10 #include <string.h>
     11 
     12 #include "r_area_list.h"
     13 #include "r_area.h"
     14 
     15 
     16 /*
     17  * Prototype internal funcs
     18  */
     19 static RAreaList *RAreaListCopy(const RAreaList *self);
     20 static void RAreaListDelete(RAreaList *self, int index);
     21 static void RAreaListAddList(RAreaList *self, const RAreaList *other);
     22 static RAreaList *RAreaListIntersectCrop(const RAreaList *self,
     23                 const RArea *area);
     24 
     25 /* Sorts and their internal comparison routines */
     26 static int _cmpX(const void *av, const void *bv);
     27 static void RAreaListSortX(const RAreaList *self);
     28 static int _cmpY(const void *av, const void *bv);
     29 static void RAreaListSortY(const RAreaList *self);
     30 
     31 
     32 
     33 /**
     34  * Create an RAreaList from a set of RArea's.
     35  * \param cap Hint as to the number of RArea's being passed
     36  * \param ... Sequence of RArea * to put in it.  Don't forget a trailing
     37  *            NULL.
     38  */
     39 RAreaList *
     40 RAreaListNew(int cap, ...)
     41 {
     42 	va_list ap;
     43 	RAreaList *list;
     44 	RArea *area;
     45 
     46 	list = malloc(sizeof(RAreaList));
     47 	if(list == NULL) {
     48 		abort();
     49 	}
     50 	list->len = 0;
     51 
     52 	if(cap <= 0) {
     53 		cap = 1;
     54 	}
     55 	list->cap = cap;
     56 	list->areas = malloc(cap * sizeof(RArea));
     57 	if(list->areas == NULL) {
     58 		abort();
     59 	}
     60 
     61 	va_start(ap, cap);
     62 
     63 	while((area = va_arg(ap, RArea *)) != NULL) {
     64 		RAreaListAdd(list, area);
     65 	}
     66 
     67 	va_end(ap);
     68 
     69 	return list;
     70 }
     71 
     72 
     73 /**
     74  * Create a copy of a given RAreaList.
     75  */
     76 static RAreaList *
     77 RAreaListCopy(const RAreaList *self)
     78 {
     79 	RAreaList *new = RAreaListNew(self->cap, NULL);
     80 
     81 	RAreaListAddList(new, self);
     82 
     83 	return new;
     84 }
     85 
     86 
     87 /**
     88  * Create a copy of an RAreaList with given amounts cropped off the
     89  * sides.  This is used principally during startup, to handle the
     90  * BorderBottom/Top/Left/Right config params.
     91  */
     92 RAreaList *
     93 RAreaListCopyCropped(const RAreaList *self, int left_margin,
     94                      int right_margin,
     95                      int top_margin, int bottom_margin)
     96 {
     97 	if(left_margin > 0 || right_margin > 0
     98 	                || top_margin > 0 || bottom_margin > 0) {
     99 		// Start with a big spanning square
    100 		RArea big_area = RAreaListBigArea(self);
    101 
    102 		// Guard against negative margins
    103 		if(left_margin < 0) {
    104 			left_margin = 0;
    105 		}
    106 		if(right_margin < 0) {
    107 			right_margin = 0;
    108 		}
    109 		if(top_margin < 0) {
    110 			top_margin = 0;
    111 		}
    112 		if(bottom_margin < 0) {
    113 			bottom_margin = 0;
    114 		}
    115 
    116 		// Squeeze down the big square by the asked for amounts
    117 		big_area.x += left_margin;
    118 		big_area.width -= left_margin + right_margin;
    119 		big_area.y += top_margin;
    120 		big_area.height -= top_margin + bottom_margin;
    121 
    122 		// If we cropped down to nothing, that's a RAreaList with nothing
    123 		// in it, so give back that.
    124 		if(big_area.width <= 0 || big_area.height <= 0) {
    125 			return RAreaListNew(0, NULL); // empty list
    126 		}
    127 
    128 		// Make a new RAreaList cropped down to that size.
    129 		return RAreaListIntersectCrop(self, &big_area);
    130 	}
    131 
    132 	// Nothing to do; our callers expect getting nothing back.
    133 	return NULL;
    134 }
    135 
    136 
    137 /**
    138  * Clean up and free an RAreaList.
    139  */
    140 void
    141 RAreaListFree(RAreaList *self)
    142 {
    143 	if(self == NULL) {
    144 		return;
    145 	}
    146 	free(self->areas);
    147 	free(self);
    148 }
    149 
    150 
    151 /**
    152  * Delete an RArea from inside an RAreaList.
    153  */
    154 static void
    155 RAreaListDelete(RAreaList *self, int index)
    156 {
    157 	if(index >= self->len) {
    158 		return;
    159 	}
    160 
    161 	self->len--;
    162 
    163 	if(index == self->len) {
    164 		return;
    165 	}
    166 
    167 	memcpy(&self->areas[index], &self->areas[index + 1],
    168 	       (self->len - index) * sizeof(RArea));
    169 }
    170 
    171 
    172 /**
    173  * Add an RArea onto an RAreaList.
    174  */
    175 void
    176 RAreaListAdd(RAreaList *self, const RArea *area)
    177 {
    178 	if(self->cap == self->len) {
    179 		RArea *new_list = realloc(self->areas, (self->cap + 1) * sizeof(RArea));
    180 		if(new_list == NULL) {
    181 			abort();
    182 		}
    183 
    184 		self->cap++;
    185 		self->areas = new_list;
    186 	}
    187 
    188 	self->areas[self->len++] = *area;
    189 }
    190 
    191 
    192 /**
    193  * Add the RArea's from one RAreaList onto another.
    194  */
    195 static void
    196 RAreaListAddList(RAreaList *self, const RAreaList *other)
    197 {
    198 	if(self->cap - self->len < other->len) {
    199 		RArea *new_list = realloc(self->areas,
    200 		                          (self->len + other->len) * sizeof(RArea));
    201 		if(new_list == NULL) {
    202 			abort();
    203 		}
    204 
    205 		self->cap = self->len + other->len;
    206 		self->areas = new_list;
    207 	}
    208 
    209 	memcpy(&self->areas[self->len], other->areas, other->len * sizeof(RArea));
    210 
    211 	self->len += other->len;
    212 }
    213 
    214 
    215 /**
    216  * qsort comparison function to sort by RArea.x
    217  */
    218 static int
    219 _cmpX(const void *av, const void *bv)
    220 {
    221 	const RArea *a = (const RArea *)av, *b = (const RArea *)bv;
    222 
    223 	if(a->x < b->x) {
    224 		return -1;
    225 	}
    226 
    227 	if(a->x > b->x) {
    228 		return 1;
    229 	}
    230 
    231 	return (a->y > b->y) - (a->y < b->y);
    232 }
    233 
    234 
    235 /**
    236  * Sort the RArea's in an RAreaList by their x coordinate.
    237  */
    238 static void
    239 RAreaListSortX(const RAreaList *self)
    240 {
    241 	if(self->len <= 1) {
    242 		return;
    243 	}
    244 
    245 	qsort(self->areas, self->len, sizeof(RArea), _cmpX);
    246 }
    247 
    248 
    249 /**
    250  * qsort comparison function to sort by RArea.t
    251  */
    252 static int
    253 _cmpY(const void *av, const void *bv)
    254 {
    255 	const RArea *a = (const RArea *)av, *b = (const RArea *)bv;
    256 
    257 	if(a->y < b->y) {
    258 		return -1;
    259 	}
    260 
    261 	if(a->y > b->y) {
    262 		return 1;
    263 	}
    264 
    265 	return (a->x > b->x) - (a->x < b->x);
    266 }
    267 
    268 
    269 /**
    270  * Sort the RArea's in an RAreaList by their y coordinate.
    271  */
    272 static void
    273 RAreaListSortY(const RAreaList *self)
    274 {
    275 	if(self->len <= 1) {
    276 		return;
    277 	}
    278 
    279 	qsort(self->areas, self->len, sizeof(RArea), _cmpY);
    280 }
    281 
    282 
    283 /**
    284  * Create an RAreaList whose RArea's are the horizontal union of our
    285  * RArea's.
    286  */
    287 RAreaList *
    288 RAreaListHorizontalUnion(const RAreaList *self)
    289 {
    290 	RAreaList *copy = RAreaListCopy(self);
    291 
    292 refine:
    293 	// Two areas can't form a horizontal stripe if there's any space
    294 	// between them.  So start by putting them all in x-coord order to be
    295 	// sure any gaps there are necessary.
    296 	RAreaListSortX(copy);
    297 
    298 	// Try HorizontalUnion'ing each area with the next one.  If we can
    299 	// create a union, replace them with it, and hop back to the top of
    300 	// the process to start over.
    301 	for(int i = 0; i < copy->len - 1; i++) {
    302 		for(int j = i + 1; j < copy->len; j++) {
    303 			RAreaList *repl = RAreaHorizontalUnion(&copy->areas[i], &copy->areas[j]);
    304 			if(repl != NULL) {
    305 				if(repl->len) {
    306 					RAreaListDelete(copy, j);
    307 					RAreaListDelete(copy, i);
    308 					RAreaListAddList(copy, repl);
    309 					RAreaListFree(repl);
    310 					goto refine;
    311 				}
    312 				RAreaListFree(repl);
    313 			}
    314 		}
    315 	}
    316 
    317 	return copy;
    318 }
    319 
    320 
    321 /**
    322  * Create an RAreaList whose RArea's are the vertical union of our
    323  * RArea's.
    324  */
    325 RAreaList *
    326 RAreaListVerticalUnion(const RAreaList *self)
    327 {
    328 	RAreaList *copy = RAreaListCopy(self);
    329 
    330 refine:
    331 	// X-ref logic above in RAreaListHorizontalUnion()
    332 	RAreaListSortY(copy);
    333 
    334 	for(int i = 0; i < copy->len - 1; i++) {
    335 		for(int j = i + 1; j < copy->len; j++) {
    336 			RAreaList *repl = RAreaVerticalUnion(&copy->areas[i], &copy->areas[j]);
    337 			if(repl != NULL) {
    338 				if(repl->len) {
    339 					RAreaListDelete(copy, j);
    340 					RAreaListDelete(copy, i);
    341 					RAreaListAddList(copy, repl);
    342 					RAreaListFree(repl);
    343 					goto refine;
    344 				}
    345 				RAreaListFree(repl);
    346 			}
    347 		}
    348 	}
    349 
    350 	return copy;
    351 }
    352 
    353 
    354 /**
    355  * Create an RAreaList of all the areas in an RAreaList that a given
    356  * RArea intersects with.
    357  */
    358 RAreaList *
    359 RAreaListIntersect(const RAreaList *self, const RArea *area)
    360 {
    361 	RAreaList *new = RAreaListNew(self->len, NULL);
    362 
    363 	for(int i = 0; i < self->len; i++) {
    364 		if(RAreaIsIntersect(&self->areas[i], area)) {
    365 			RAreaListAdd(new, &self->areas[i]);
    366 		}
    367 	}
    368 
    369 	return new;
    370 }
    371 
    372 
    373 /**
    374  * Run a function over each RArea in an RAreaList until one returns true,
    375  * allowing them a place to stash other internal data.
    376  */
    377 void
    378 RAreaListForeach(const RAreaList *self,
    379                  bool (*func)(const RArea *cur_area, void *data),
    380                  void *data)
    381 {
    382 	for(int i = 0 ; i < self->len ; i++) {
    383 		if(func(&(self->areas[i]), data) == true) {
    384 			break;
    385 		}
    386 	}
    387 }
    388 
    389 
    390 
    391 /**
    392  * Create an RAreaList from another, cropped to a certain area defined by
    393  * an RArea.
    394  */
    395 static RAreaList *
    396 RAreaListIntersectCrop(const RAreaList *self, const RArea *area)
    397 {
    398 	RAreaList *new = RAreaListNew(self->len, NULL);
    399 
    400 	for(int i = 0; i < self->len; i++) {
    401 		RArea it = RAreaIntersect(&self->areas[i], area);
    402 		if(RAreaIsValid(&it)) {
    403 			RAreaListAdd(new, &it);
    404 		}
    405 	}
    406 
    407 	return new;
    408 }
    409 
    410 
    411 /**
    412  * Create a maximal RArea describing the union of an RAreaList.
    413  *
    414  * This is used to construct a giant square that contains all our
    415  * monitors (and the dead area necessary to cover them).  It winds up
    416  * being the equivalent of a spanning pseudo-Root window, and is used
    417  * when we need to figure some sort of "overall" positioning, like when
    418  * figuring "real" x/y coordinates.
    419  */
    420 RArea
    421 RAreaListBigArea(const RAreaList *self)
    422 {
    423 	int x, y, x2, y2;
    424 
    425 	// Guard; probably impossible
    426 	if(self->len < 1) {
    427 		return RAreaInvalid();
    428 	}
    429 
    430 	for(int i = 0 ; i < self->len ; i++) {
    431 		const RArea *area = &(self->areas[i]);
    432 		if(i == 0 || area->x < x) {
    433 			x = area->x;
    434 		}
    435 
    436 		if(i == 0 || area->y < y) {
    437 			y = area->y;
    438 		}
    439 
    440 		if(i == 0 || RAreaX2(area) > x2) {
    441 			x2 = RAreaX2(area);
    442 		}
    443 
    444 		if(i == 0 || RAreaY2(area) > y2) {
    445 			y2 = RAreaY2(area);
    446 		}
    447 	}
    448 
    449 	return RAreaNew(x, y, x2 - x + 1, y2 - y + 1);
    450 }
    451 
    452 
    453 /**
    454  * Find the RArea in an RAreaList that has the largest intersection with
    455  * a given RArea.  Colloquially, which area in an RAreaList does our
    456  * RArea mostly fit into?  This is used to resize a window to fill one
    457  * monitor, by finding which monitor it's on.
    458  */
    459 RArea
    460 RAreaListBestTarget(const RAreaList *self, const RArea *area)
    461 {
    462 	RArea full_area = RAreaInvalid();
    463 	int max_area = -1;
    464 
    465 	for(int i = 0; i < self->len; i++) {
    466 		RArea it = RAreaIntersect(area, &self->areas[i]);
    467 		if(RAreaIsValid(&it) && (max_area < 0 || RAreaArea(&it) > max_area)) {
    468 			max_area = RAreaArea(&it);
    469 			full_area = self->areas[i];
    470 		}
    471 	}
    472 
    473 	return full_area;
    474 }
    475 
    476 
    477 /**
    478  * Find the x coordinate of the right-most RArea in an RAreaList.
    479  */
    480 int
    481 RAreaListMaxX(const RAreaList *self)
    482 {
    483 	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
    484 	int max_x = self->len ? cur_area->x : 0;
    485 
    486 	// While a for(i=0 ; i<self->len ; i++) loop is generally nicer for
    487 	// these iterations, it winds up being a little trickier here, so we
    488 	// leave it as a pointer-stepper.
    489 	while(++cur_area < area_end) {
    490 		if(cur_area->x > max_x) {
    491 			max_x = cur_area->x;
    492 		}
    493 	}
    494 
    495 	return max_x;
    496 }
    497 
    498 
    499 /**
    500  * Find the y coordinate of the bottom-most RArea in an RAreaList.
    501  */
    502 int
    503 RAreaListMaxY(const RAreaList *self)
    504 {
    505 	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
    506 	int max_y = self->len ? cur_area->y : 0;
    507 
    508 	while(++cur_area < area_end) {
    509 		if(cur_area->y > max_y) {
    510 			max_y = cur_area->y;
    511 		}
    512 	}
    513 
    514 	return max_y;
    515 }
    516 
    517 
    518 /**
    519  * Find the x coordinate of the right edge of the left-most RArea in an
    520  * RAreaList.
    521  */
    522 int
    523 RAreaListMinX2(const RAreaList *self)
    524 {
    525 	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
    526 	int min_x = self->len ? RAreaX2(cur_area) : 0;
    527 
    528 	while(++cur_area < area_end) {
    529 		if(RAreaX2(cur_area) < min_x) {
    530 			min_x = RAreaX2(cur_area);
    531 		}
    532 	}
    533 
    534 	return min_x;
    535 }
    536 
    537 
    538 /**
    539  * Find the y coordinate of the bottom edge of the top-most RArea in an
    540  * RAreaList.
    541  */
    542 int
    543 RAreaListMinY2(const RAreaList *self)
    544 {
    545 	RArea *cur_area = &self->areas[0], *area_end = &self->areas[self->len];
    546 	int min_y = self->len ? RAreaY2(cur_area) : 0;
    547 
    548 	while(++cur_area < area_end) {
    549 		if(RAreaY2(cur_area) < min_y) {
    550 			min_y = RAreaY2(cur_area);
    551 		}
    552 	}
    553 
    554 	return min_y;
    555 }
    556 
    557 
    558 /**
    559  * Pretty-print an RAreaList.
    560  *
    561  * Used for dev/debug.
    562  */
    563 void
    564 RAreaListPrint(const RAreaList *self)
    565 {
    566 	fprintf(stderr, "[len=%d cap=%d", self->len, self->cap);
    567 
    568 	for(int i = 0 ; i < self->len ; i++) {
    569 		RArea *area = &self->areas[i];
    570 		fprintf(stderr, " ");
    571 		RAreaPrint(area);
    572 	}
    573 
    574 	fprintf(stderr, "]");
    575 }
    576