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(©->areas[i], ©->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(©->areas[i], ©->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