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 */
19static RAreaList *RAreaListCopy(const RAreaList *self);
20static void RAreaListDelete(RAreaList *self, int index);
21static void RAreaListAddList(RAreaList *self, const RAreaList *other);
22static RAreaList *RAreaListIntersectCrop(const RAreaList *self,
23                const RArea *area);
24
25/* Sorts and their internal comparison routines */
26static int _cmpX(const void *av, const void *bv);
27static void RAreaListSortX(const RAreaList *self);
28static int _cmpY(const void *av, const void *bv);
29static 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 */
39RAreaList *
40RAreaListNew(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 */
76static RAreaList *
77RAreaListCopy(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 */
92RAreaList *
93RAreaListCopyCropped(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 */
140void
141RAreaListFree(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 */
154static void
155RAreaListDelete(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 */
175void
176RAreaListAdd(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 */
195static void
196RAreaListAddList(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 */
218static 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 */
238static void
239RAreaListSortX(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 */
252static 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 */
272static void
273RAreaListSortY(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 */
287RAreaList *
288RAreaListHorizontalUnion(const RAreaList *self)
289{
290	RAreaList *copy = RAreaListCopy(self);
291
292refine:
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 */
325RAreaList *
326RAreaListVerticalUnion(const RAreaList *self)
327{
328	RAreaList *copy = RAreaListCopy(self);
329
330refine:
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 */
358RAreaList *
359RAreaListIntersect(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 */
377void
378RAreaListForeach(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 */
395static RAreaList *
396RAreaListIntersectCrop(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 */
420RArea
421RAreaListBigArea(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 */
459RArea
460RAreaListBestTarget(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 */
480int
481RAreaListMaxX(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 */
502int
503RAreaListMaxY(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 */
522int
523RAreaListMinX2(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 */
542int
543RAreaListMinY2(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 */
563void
564RAreaListPrint(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