Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright notice...
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <stdlib.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 
     11 #include "r_layout.h"
     12 #include "r_area_list.h"
     13 #include "r_area.h"
     14 #include "util.h"
     15 
     16 
     17 /*
     18  * Prototype internal funcs
     19  */
     20 static void _RLayoutFreeNames(RLayout *self);
     21 static RAreaList *_RLayoutRecenterVertically(const RLayout *self,
     22                 const RArea *far_area);
     23 static RAreaList *_RLayoutRecenterHorizontally(const RLayout *self,
     24                 const RArea *far_area);
     25 static RAreaList *_RLayoutVerticalIntersect(const RLayout *self,
     26                 const RArea *area);
     27 static RAreaList *_RLayoutHorizontalIntersect(const RLayout *self,
     28                 const RArea *area);
     29 
     30 /* Foreach() callbacks used in various lookups */
     31 static bool _findMonitorByXY(const RArea *cur, void *vdata);
     32 static bool _findMonitorBottomEdge(const RArea *cur, void *vdata);
     33 static bool _findMonitorTopEdge(const RArea *cur, void *vdata);
     34 static bool _findMonitorLeftEdge(const RArea *cur, void *vdata);
     35 static bool _findMonitorRightEdge(const RArea *cur, void *vdata);
     36 
     37 
     38 
     39 
     40 /************************
     41  *
     42  * First, some funcs for creating and destroying RLayout's in various
     43  * ways.
     44  *
     45  ************************/
     46 
     47 
     48 /**
     49  * Create an RLayout for a given set of monitors.
     50  *
     51  * This stashes up the list of monitors, and precalculates the
     52  * horizontal/vertical stripes that compose it.
     53  */
     54 RLayout *
     55 RLayoutNew(RAreaList *monitors)
     56 {
     57 	RLayout *layout = malloc(sizeof(RLayout));
     58 	if(layout == NULL) {
     59 		abort();
     60 	}
     61 
     62 	layout->monitors = monitors;
     63 	layout->horiz = RAreaListHorizontalUnion(monitors);
     64 	layout->vert = RAreaListVerticalUnion(monitors);
     65 	layout->names = NULL;
     66 
     67 	return layout;
     68 }
     69 
     70 
     71 /**
     72  * Create a copy of an RLayout with given amounts cropped off the sides.
     73  * This is used anywhere we need to pretend our display area is smaller
     74  * than it actually is (e.g., via the BorderBottom/Top/Left/Right config
     75  * params)
     76  */
     77 RLayout *
     78 RLayoutCopyCropped(const RLayout *self, int left_margin, int right_margin,
     79                    int top_margin, int bottom_margin)
     80 {
     81 	RAreaList *cropped_monitors = RAreaListCopyCropped(self->monitors,
     82 	                              left_margin, right_margin,
     83 	                              top_margin, bottom_margin);
     84 	if(cropped_monitors == NULL) {
     85 		return NULL;        // nothing to crop, same layout as passed
     86 	}
     87 
     88 	return RLayoutNew(cropped_monitors);
     89 }
     90 
     91 
     92 /**
     93  * Clean up and free any RLayout.names there might be in an RLayout.
     94  */
     95 static void
     96 _RLayoutFreeNames(RLayout *self)
     97 {
     98 	if(self == NULL) {
     99 		return;
    100 	}
    101 	if(self->names != NULL) {
    102 		free(self->names);
    103 		self->names = NULL;
    104 	}
    105 }
    106 
    107 
    108 /**
    109  * Clean up and free an RLayout.
    110  */
    111 void
    112 RLayoutFree(RLayout *self)
    113 {
    114 	if(self == NULL) {
    115 		return;
    116 	}
    117 
    118 	RAreaListFree(self->monitors);
    119 	RAreaListFree(self->horiz);
    120 	RAreaListFree(self->vert);
    121 	_RLayoutFreeNames(self);
    122 	free(self);
    123 }
    124 
    125 
    126 /**
    127  * Set the names for our monitors in an RLayout.  This is only used for
    128  * the RLayout that describes our complete monitor layout, which fills in
    129  * the RANDR names for each output.
    130  */
    131 RLayout *
    132 RLayoutSetMonitorsNames(RLayout *self, char **names)
    133 {
    134 	_RLayoutFreeNames(self);
    135 	self->names = names;
    136 	return self;
    137 }
    138 
    139 
    140 
    141 /************************
    142  *
    143  * Next, a few util funcs for dealing with RArea's that are outside our
    144  * RLayout, but we want to find the nearest way to move them inside, then
    145  * return a list of which RArea's they'd be intersecting with.
    146  *
    147  ************************/
    148 
    149 
    150 /**
    151  * Given an RArea that doesn't reside in any of the areas in our RLayout,
    152  * create a list of maximally-tall RArea slices out of our layout where
    153  * it would wind up if we brought it onto the nearest screen edge.  This
    154  * yields a RAreaList as tall as the slice[es] the window would touch if
    155  * we moved it in.
    156  *
    157  * If we had the move the window horizontally (it was off-screen to the
    158  * right or left), it results in a 1-pixel-wide slice of the right- or
    159  * left-most self->vert.
    160  *
    161  * If we had to move it vertically (it was off to the top or bottom), it
    162  * winds up being whatever horizontal intersection with self->vert would
    163  * result from the window's x and width, with the full height of the
    164  * involved slices.
    165  *
    166  * This is the vertical-stripe-returning counterpart of
    167  * _RLayoutRecenterHorizontally().
    168  *
    169  * This is called only by \_RLayoutVerticalIntersect() when given an RArea
    170  * that doesn't already intersect the RLayout.  Will probably not tell
    171  * you something useful if given a far_area that already _does_ intersect
    172  * self.
    173  *
    174  * \param self     Our current monitor layout
    175  * \param far_area The area to act on
    176  */
    177 static RAreaList *
    178 _RLayoutRecenterVertically(const RLayout *self, const RArea *far_area)
    179 {
    180 	RArea big = RAreaListBigArea(self->monitors), tmp;
    181 
    182 	// We assume far_area is outside of self.  So it's in one of the
    183 	// three labelled areas:
    184 	//
    185 	//  |_V_|   ___ tmp.top
    186 	//  |   |     |
    187 	// L|   |R    |
    188 	//  |___|   ___ tmp.bottom
    189 	//  | V |
    190 	//
    191 	// So we'll create an RArea that's the y and height of big (a giant
    192 	// rectangle covering all the monitors), so that its intersection
    193 	// with self->vert will always cover a full vertical stripe.  Then
    194 	// we'll set its x/width so that it's shifted to be at least
    195 	// minimally inside big somehow.
    196 
    197 	// Where did it wind up?
    198 	if((far_area->x >= big.x && far_area->x <= RAreaX2(&big))
    199 	                || (RAreaX2(far_area) >= big.x && RAreaX2(far_area) <= RAreaX2(&big))) {
    200 		// In one of the V areas.  It's already in a horizontal position
    201 		// that would fit, so we just keep x/width.
    202 		tmp = RAreaNew(far_area->x, big.y,
    203 		               far_area->width, big.height);
    204 	}
    205 	else if(RAreaX2(far_area) < big.x) {
    206 		// Off to the left side in L, so move it over just far enough
    207 		// that 1 pixel of it protrudes into the left side.
    208 		tmp = RAreaNew(big.x - far_area->width + 1, big.y,
    209 		               far_area->width, big.height);
    210 	}
    211 	else {
    212 		// Off to the right side in R, so move it over just far enough
    213 		// that 1 pixel of it protrudes into the right side.
    214 		tmp = RAreaNew(RAreaX2(&big), big.y,
    215 		               far_area->width, big.height);
    216 	}
    217 
    218 	// Then intersect that (full height, at least 1 pixel horizontally
    219 	// somewhere) with our collection of vertical stripes, to yield an
    220 	// answer.  If the window was off to the left or right, this will
    221 	// yield a 1-pixel-wide slice of either the left- or right-most
    222 	// ->vert of our layout.  If it were off the top of bottom, though,
    223 	// it'll yield some slice of 1 (or more) of our ->vert's, as wide as
    224 	// the window itself was.
    225 	return RAreaListIntersect(self->vert, &tmp);
    226 
    227 	// n.b.; _RLayoutRecenterHorizontally() is the counterpart to this
    228 	// with horizontal slices.  The comments in the two have been written
    229 	// independently with somewhat different explanatory styles, so if
    230 	// the description here was confusing, try reading the other one and
    231 	// transposing.
    232 }
    233 
    234 
    235 /**
    236  * Given an RArea that doesn't reside in any of the areas in our RLayout,
    237  * create a list of maximally-wide RArea slices out of our layout where
    238  * it would wind up if we brought it onto the nearest screen edge.  This
    239  * yields a RAreaList as wide as the slice[es] the window would touch if
    240  * we moved it in.
    241  *
    242  * If we had the move the window vertically (it was off-screen to the top
    243  * or bottom), it results in a 1-pixel-wide slice of the top- or
    244  * bottom-most self->horiz.
    245  *
    246  * If we had to move it horizontally (it was off to the left or right),
    247  * it winds up being whatever vertical intersection with self->horiz
    248  * would result from the window's y and height, with the full width of
    249  * the involved slices.
    250  *
    251  * This is the horizontal-stripe-returning counterpart of
    252  * _RLayoutRecenterVertically().
    253  *
    254  * This is called only by \_RLayoutVerticalIntersect() when given an RArea
    255  * that doesn't already intersect the RLayout.  Will probably not tell
    256  * you something useful if given a far_area that already _does_ intersect
    257  * self.
    258  *
    259  * \param self     Our current monitor layout
    260  * \param far_area The area to act on
    261  */
    262 static RAreaList *
    263 _RLayoutRecenterHorizontally(const RLayout *self, const RArea *far_area)
    264 {
    265 	RArea big = RAreaListBigArea(self->monitors), tmp;
    266 
    267 	// far_area is outside self, so it's in one of the 3 labelled areas:
    268 	//
    269 	// ___T___
    270 	//  |   |
    271 	// H|   |H
    272 	// _|___|_
    273 	//    B
    274 	//
    275 	// We create an RArea that's the x and width of big, so it always
    276 	// covers the entire width of any member of ->horiz.  Then we move
    277 	// the far_area in to the nearest edge to figure the y/height to set.
    278 
    279 	if((far_area->y >= big.y && far_area->y <= RAreaY2(&big))
    280 	                || (RAreaY2(far_area) >= big.y && RAreaY2(far_area) <= RAreaY2(&big))) {
    281 		// In one of the H areas.  Already in a valid place vertically,
    282 		// so make a horizontal strip that position/tall.
    283 		tmp = RAreaNew(big.x, far_area->y,
    284 		               big.width, far_area->height);
    285 	}
    286 	else if(RAreaY2(far_area) < big.y) {
    287 		// Off the top (T); move it down just far enough that it's bottom
    288 		// protrudes 1 pixel into the top.
    289 		tmp = RAreaNew(big.x, big.y - far_area->height + 1,
    290 		               big.width, far_area->height);
    291 	}
    292 	else {
    293 		// Off the bottom (B); move it up just enough that it's top
    294 		// protrudes 1 pixel into the bottom.
    295 		tmp = RAreaNew(big.x, RAreaY2(&big),
    296 		               big.width, far_area->height);
    297 	}
    298 
    299 	// And intersect that RArea with self->horiz.  This results in a
    300 	// full-width overlap with 1 pixel at the bottom of the bottom-most,
    301 	// 1 pixel at the top of the top-most, or 1..(far_area->height)
    302 	// overlap somewhere.  In that last case (far_area was in H), the
    303 	// intersection may yield multiple areas.
    304 	return RAreaListIntersect(self->horiz, &tmp);
    305 
    306 	// n.b.; _RLayoutRecenterVertically() is the counterpart to this with
    307 	// vertical slices.  The comments in the two have been written
    308 	// independently with somewhat different explanatory styles, so if
    309 	// the description here was confusing, try reading the other one and
    310 	// transposing.
    311 }
    312 
    313 
    314 
    315 /************************
    316  *
    317  * Some wrappers called when we need to Insersect an RArea with our
    318  * RLayout, but also handle the case (using the above funcs) when the
    319  * RArea doesn't Intersect our layout by finding the nearest border we
    320  * could shuffle it over.
    321  *
    322  ************************/
    323 
    324 
    325 /**
    326  * Find which vertical regions of our monitor layout a given RArea (often
    327  * a window) is in.  If it's completely off the screen, we move it until
    328  * it's just over the nearest edge, and return the vertical stripe(s) it
    329  * would be in then.
    330  *
    331  * This function is used only by RLayoutFindTopBottomEdges()
    332  */
    333 static RAreaList *
    334 _RLayoutVerticalIntersect(const RLayout *self, const RArea *area)
    335 {
    336 	RAreaList *mit = RAreaListIntersect(self->vert, area);
    337 
    338 	if(mit->len == 0) {
    339 		// Not on screen.  Move it to just over the nearest edge so it
    340 		// is, and give the slices it's in then.
    341 		RAreaListFree(mit);
    342 		mit = _RLayoutRecenterVertically(self, area);
    343 	}
    344 	return mit;
    345 }
    346 
    347 
    348 /**
    349  * Find which horizontal regions of our monitor layout a given RArea
    350  * (often a window) is in.  If it's completely off the screen, we move it
    351  * until it's just over the nearest edge, and return the horizontal
    352  * stripe(s) it would be in then.
    353  *
    354  * This function is used only by RLayoutFindLeftRightEdges()
    355  */
    356 static RAreaList *
    357 _RLayoutHorizontalIntersect(const RLayout *self, const RArea *area)
    358 {
    359 	RAreaList *mit = RAreaListIntersect(self->horiz, area);
    360 
    361 	if(mit->len == 0) {
    362 		// Not on screen.  Move it to just over the nearest edge so it
    363 		// is, and give the slices it's in then.
    364 		RAreaListFree(mit);
    365 		mit = _RLayoutRecenterHorizontally(self, area);
    366 	}
    367 
    368 	return mit;
    369 }
    370 
    371 
    372 
    373 /************************
    374  *
    375  * Some funcs using the above (layers of) utils to find info about which
    376  * stripes of the RLayout an Area appears in.   These are used mostly as
    377  * backend utils for figuring various f.*zoom's.
    378  *
    379  ************************/
    380 
    381 
    382 /**
    383  * Figure the position (or nearest practical position) of an area in our
    384  * screen layout, and return info about the bottom/top stripes it fits
    385  * into.
    386  *
    387  * Note that the return values (params) are slightly counterintuitive;
    388  * top tells you where the top of the lowest stripe that area intersects
    389  * with is, and bottom tells you the bottom of the highest.
    390  *
    391  * This is used as a backend piece of various calculations trying to be
    392  * sure something winds up on-screen and when figuring out how to zoom
    393  * it.
    394  *
    395  * \param[in]  self   The monitor layout to work from
    396  * \param[in]  area   The area to be fit into the monitors
    397  * \param[out] top    The top of the lowest stripe area fits into.
    398  * \param[out] bottom The bottom of the highest stripe area fits into.
    399  */
    400 void
    401 RLayoutFindTopBottomEdges(const RLayout *self, const RArea *area, int *top,
    402                           int *bottom)
    403 {
    404 	RAreaList *mit = _RLayoutVerticalIntersect(self, area);
    405 
    406 	if(top != NULL) {
    407 		*top = RAreaListMaxY(mit);
    408 	}
    409 
    410 	if(bottom != NULL) {
    411 		*bottom = RAreaListMinY2(mit);
    412 	}
    413 
    414 	RAreaListFree(mit);
    415 }
    416 
    417 
    418 /**
    419  * Find the bottom of the top stripe of self that area fits into.  A
    420  * shortcut to get only the second return value of
    421  * RLayoutFindTopBottomEdges().
    422  */
    423 int
    424 RLayoutFindBottomEdge(const RLayout *self, const RArea *area)
    425 {
    426 	int min_y2;
    427 	RLayoutFindTopBottomEdges(self, area, NULL, &min_y2);
    428 	return min_y2;
    429 }
    430 
    431 
    432 /**
    433  * Find the top of the bottom stripe of self that area fits into.  A
    434  * shortcut to get only the first return value of
    435  * RLayoutFindTopBottomEdges().
    436  */
    437 int
    438 RLayoutFindTopEdge(const RLayout *self, const RArea *area)
    439 {
    440 	int max_y;
    441 	RLayoutFindTopBottomEdges(self, area, &max_y, NULL);
    442 	return max_y;
    443 }
    444 
    445 
    446 /**
    447  * Figure the position (or nearest practical position) of an area in our
    448  * screen layout, and return info about the left/rightmost stripes it fits
    449  * into.
    450  *
    451  * As with RLayoutFindTopBottomEdges(), the return values (params) are
    452  * slightly counterintuitive.  left tells you where the left-side of the
    453  * right-most stripe that area intersects with is, and right tells you
    454  * the right side of the left-most.
    455  *
    456  * This is used as a backend piece of various calculations trying to be
    457  * sure something winds up on-screen and when figuring out how to zoom
    458  * it.
    459  *
    460  * \param[in]  self   The monitor layout to work from
    461  * \param[in]  area   The area to be fit into the monitors
    462  * \param[out] left   The left edge of the right-most stripe area fits into.
    463  * \param[out] right  The right edge of the left-most stripe area fits into.
    464  */
    465 void
    466 RLayoutFindLeftRightEdges(const RLayout *self, const RArea *area, int *left,
    467                           int *right)
    468 {
    469 	RAreaList *mit = _RLayoutHorizontalIntersect(self, area);
    470 
    471 	if(left != NULL) {
    472 		*left = RAreaListMaxX(mit);
    473 	}
    474 
    475 	if(right != NULL) {
    476 		*right = RAreaListMinX2(mit);
    477 	}
    478 
    479 	RAreaListFree(mit);
    480 }
    481 
    482 
    483 /**
    484  * Find the left edge of the right-most stripe of self that area fits
    485  * into.  A shortcut to get only the first return value of
    486  * RLayoutFindLeftRightEdges().
    487  */
    488 int
    489 RLayoutFindLeftEdge(const RLayout *self, const RArea *area)
    490 {
    491 	int max_x;
    492 	RLayoutFindLeftRightEdges(self, area, &max_x, NULL);
    493 	return max_x;
    494 }
    495 
    496 
    497 /**
    498  * Find the right edge of the left-most stripe of self that area fits
    499  * into.  A shortcut to get only the second return value of
    500  * RLayoutFindLeftRightEdges().
    501  */
    502 int
    503 RLayoutFindRightEdge(const RLayout *self, const RArea *area)
    504 {
    505 	int min_x2;
    506 	RLayoutFindLeftRightEdges(self, area, NULL, &min_x2);
    507 	return min_x2;
    508 }
    509 
    510 
    511 
    512 /************************
    513  *
    514  * Lookups to find areas in an RLayout by various means.
    515  *
    516  ************************/
    517 
    518 
    519 /// Internal structure for callback in RLayoutGetAreaAtXY().
    520 struct monitor_finder_xy {
    521 	const RArea *area;
    522 	int x, y;
    523 };
    524 
    525 /// Callback util for RLayoutGetAreaAtXY().
    526 static bool
    527 _findMonitorByXY(const RArea *cur, void *vdata)
    528 {
    529 	struct monitor_finder_xy *data = (struct monitor_finder_xy *)vdata;
    530 
    531 	if(RAreaContainsXY(cur, data->x, data->y)) {
    532 		data->area = cur;
    533 		return true;
    534 	}
    535 	return false;
    536 }
    537 
    538 /**
    539  * Find the RArea in a RLayout that a given coordinate falls into.  In
    540  * practice, the RArea's in self are the monitors of the desktop, so this
    541  * answers "Which monitor is this position on?"
    542  */
    543 RArea
    544 RLayoutGetAreaAtXY(const RLayout *self, int x, int y)
    545 {
    546 	struct monitor_finder_xy data = { .area = NULL, .x = x, .y = y };
    547 
    548 	RAreaListForeach(self->monitors, _findMonitorByXY, &data);
    549 
    550 	return data.area == NULL ? self->monitors->areas[0] : *data.area;
    551 }
    552 
    553 
    554 /**
    555  * Return the index'th RArea in an RLayout, or RAreaInvalid() with an out
    556  * of range index.
    557  */
    558 RArea
    559 RLayoutGetAreaIndex(const RLayout *self, int index)
    560 {
    561 	if(index >= self->monitors->len || index < 0) {
    562 		return RAreaInvalid();
    563 	}
    564 
    565 	return self->monitors->areas[index];
    566 }
    567 
    568 
    569 /**
    570  * Return the RArea in self with the name given by the string of length
    571  * len at name.  This is only used in RLayoutXParseGeometry() to parse a
    572  * fragment of a larger string, hence the need for len.  It's used to
    573  * find the monitor with a given name (RANDR output name).
    574  */
    575 RArea
    576 RLayoutGetAreaByName(const RLayout *self, const char *name, int len)
    577 {
    578 	if(self->names != NULL) {
    579 		if(len < 0) {
    580 			len = strlen(name);
    581 		}
    582 
    583 		for(int i = 0; i < self->monitors->len
    584 		                && self->names[i] != NULL; i++) {
    585 			if(strncmp(self->names[i], name, len) == 0) {
    586 				return self->monitors->areas[i];
    587 			}
    588 		}
    589 	}
    590 
    591 	return RAreaInvalid();
    592 }
    593 
    594 
    595 
    596 /************************
    597  *
    598  * Now some utils for finding various edges of the monitors a given RArea
    599  * intersects with.
    600  *
    601  ************************/
    602 
    603 
    604 /// Internal struct for use in FindMonitor*Edge() callbacks.
    605 struct monitor_edge_finder {
    606 	const RArea *area;
    607 	union {
    608 		int max_x;
    609 		int max_y;
    610 		int min_x2;
    611 		int min_y2;
    612 	} u;
    613 	bool found;
    614 };
    615 
    616 /// Callback util for RLayoutFindMonitorBottomEdge()
    617 static bool
    618 _findMonitorBottomEdge(const RArea *cur, void *vdata)
    619 {
    620 	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
    621 
    622 	// Does the area we're looking for intersect this piece of the
    623 	// RLayout, is the bottom of the area shown on it, and is the bottom
    624 	// of this piece the highest we've yet found that satisfies those
    625 	// conditions?
    626 	if(RAreaIsIntersect(cur, data->area)
    627 	                && RAreaY2(cur) > RAreaY2(data->area)
    628 	                && (!data->found || RAreaY2(cur) < data->u.min_y2)) {
    629 		data->u.min_y2 = RAreaY2(cur);
    630 		data->found = true;
    631 	}
    632 	return false;
    633 }
    634 
    635 /**
    636  * Find the bottom edge of the top-most monitor that contains the most of
    637  * a given RArea.  Generally, the area would be a window.
    638  *
    639  * That is, we find the monitor whose bottom is the highest up, but that
    640  * still shows the bottom edge of the window, and return that monitor's
    641  * bottom.  If the bottom of the window is off all the monitors, that's
    642  * just the highest-ending monitor that contains the window.
    643  */
    644 int
    645 RLayoutFindMonitorBottomEdge(const RLayout *self, const RArea *area)
    646 {
    647 	struct monitor_edge_finder data = { .area = area };
    648 
    649 	RAreaListForeach(self->monitors, _findMonitorBottomEdge, &data);
    650 
    651 	return data.found ? data.u.min_y2 : RLayoutFindBottomEdge(self, area);
    652 }
    653 
    654 
    655 /// Callback util for RLayoutFindMonitorTopEdge()
    656 static bool
    657 _findMonitorTopEdge(const RArea *cur, void *vdata)
    658 {
    659 	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
    660 
    661 	// Does the area we're looking for intersect this piece of the
    662 	// RLayout, is the top of the area shown on it, and is the top
    663 	// of this piece the lowest we've yet found that satisfies those
    664 	// conditions?
    665 	if(RAreaIsIntersect(cur, data->area)
    666 	                && cur->y < data->area->y
    667 	                && (!data->found || cur->y > data->u.max_y)) {
    668 		data->u.max_y = cur->y;
    669 		data->found = true;
    670 	}
    671 	return false;
    672 }
    673 
    674 /**
    675  * Find the top edge of the bottom-most monitor that contains the most of
    676  * a given RArea.  Generally, the area would be a window.
    677  *
    678  * That is, we find the monitor whose top is the lowest down, but that
    679  * still shows the top edge of the window, and return that monitor's top.
    680  * If the top of the window is off all the monitors, that's just the
    681  * lowest-ending monitor that contains part of the window.
    682  */
    683 int
    684 RLayoutFindMonitorTopEdge(const RLayout *self, const RArea *area)
    685 {
    686 	struct monitor_edge_finder data = { .area = area };
    687 
    688 	RAreaListForeach(self->monitors, _findMonitorTopEdge, &data);
    689 
    690 	return data.found ? data.u.max_y : RLayoutFindTopEdge(self, area);
    691 }
    692 
    693 
    694 /// Callback util for RLayoutFindMonitorLeftEdge()
    695 static bool
    696 _findMonitorLeftEdge(const RArea *cur, void *vdata)
    697 {
    698 	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
    699 
    700 	// Does the area we're looking for intersect this piece of the
    701 	// RLayout, is the left of the area shown on it, and is the left of
    702 	// this piece the right-most we've yet found that satisfies those
    703 	// conditions?
    704 	if(RAreaIsIntersect(cur, data->area)
    705 	                && cur->x < data->area->x
    706 	                && (!data->found || cur->x > data->u.max_x)) {
    707 		data->u.max_x = cur->x;
    708 		data->found = true;
    709 	}
    710 	return false;
    711 }
    712 
    713 /**
    714  * Find the left edge of the right-most monitor that contains the most of
    715  * a given RArea.  Generally, the area would be a window.
    716  *
    717  * That is, we find the monitor whose left is the furthest right, but
    718  * that still shows the left edge of the window, and return that
    719  * monitor's left.  If the left edge of the window is off all the
    720  * monitors, that's just the right-most-ending monitor that contains the
    721  * window.
    722  */
    723 int
    724 RLayoutFindMonitorLeftEdge(const RLayout *self, const RArea *area)
    725 {
    726 	struct monitor_edge_finder data = { .area = area };
    727 
    728 	RAreaListForeach(self->monitors, _findMonitorLeftEdge, &data);
    729 
    730 	return data.found ? data.u.max_x : RLayoutFindLeftEdge(self, area);
    731 }
    732 
    733 
    734 /// Callback util for RLayoutFindMonitorRightEdge()
    735 static bool
    736 _findMonitorRightEdge(const RArea *cur, void *vdata)
    737 {
    738 	struct monitor_edge_finder *data = (struct monitor_edge_finder *)vdata;
    739 
    740 	// Does the area we're looking for intersect this piece of the
    741 	// RLayout, is the right of the area shown on it, and is the right of
    742 	// this piece the left-most we've yet found that satisfies those
    743 	// conditions?
    744 	if(RAreaIsIntersect(cur, data->area)
    745 	                && RAreaX2(cur) > RAreaX2(data->area)
    746 	                && (!data->found || RAreaX2(cur) < data->u.min_x2)) {
    747 		data->u.min_x2 = RAreaX2(cur);
    748 		data->found = true;
    749 	}
    750 	return false;
    751 }
    752 
    753 /**
    754  * Find the right edge of the left-most monitor that contains the most of
    755  * a given RArea.  Generally, the area would be a window.
    756  *
    757  * That is, we find the monitor whose right is the furthest left, but
    758  * that still shows the right edge of the window, and return that
    759  * monitor's right.  If the right edge of the window is off all the
    760  * monitors, that's just the left-most-ending monitor that contains the
    761  * window.
    762  */
    763 int
    764 RLayoutFindMonitorRightEdge(const RLayout *self, const RArea *area)
    765 {
    766 	struct monitor_edge_finder data = { .area = area };
    767 
    768 	RAreaListForeach(self->monitors, _findMonitorRightEdge, &data);
    769 
    770 	return data.found ? data.u.min_x2 : RLayoutFindRightEdge(self, area);
    771 }
    772 
    773 
    774 
    775 /************************
    776  *
    777  * Backend funcs called by the f.*zoom handlers to figure the area we
    778  * should zoom into.
    779  *
    780  ************************/
    781 
    782 
    783 /**
    784  * Figure the best way to stretch an area across the full horizontal
    785  * width of an RLayout.  This is the backend for the f.xhorizoom ctwm
    786  * function, zooming a window to the full width of all monitors.
    787  */
    788 RArea
    789 RLayoutFullHoriz(const RLayout *self, const RArea *area)
    790 {
    791 	int max_x, min_x2;
    792 
    793 	RLayoutFindLeftRightEdges(self, area, &max_x, &min_x2);
    794 
    795 	return RAreaNew(max_x, area->y, min_x2 - max_x + 1, area->height);
    796 
    797 	/**
    798 	 * This yields an area:
    799 	 * ~~~
    800 	 * TL   W
    801 	 *   *-----*
    802 	 *   |     |
    803 	 *  H|     |
    804 	 *   |     |
    805 	 *   *-----*
    806 	 * ~~~
    807 	 *
    808 	 * The precise construction of the area can be tricky.
    809 	 *
    810 	 * In the simplest case, the area is entirely in one horizontal
    811 	 * stripe to start with.  In that case, max_x is the left side of
    812 	 * that box, min_x2 is the right side, so the resulting area starts
    813 	 * at (left margin, area y), with the height of y and the width of
    814 	 * the whole stripe.  Easy.
    815 	 *
    816 	 * When it spans multiple, it's more convoluted.  Let's consider an
    817 	 * example layout (of horizontal stripes, so that top stripe may be
    818 	 * across 2 monitors) to make it a little clearer:
    819 	 *
    820 	 * ~~~
    821 	 * *--------------------------*
    822 	 * |             |......2.....|
    823 	 * |                          |  <-----.
    824 	 * |             1 =========  |         .
    825 	 * *-------------*-=========--*-*        >-- 2 horiz stripes
    826 	 *               | =========    |       '
    827 	 *               |  /           |  <---'
    828 	 *       area  --+-'            |
    829 	 *               *--------------*
    830 	 * ~~~
    831 	 *
    832 	 * So in this case, we're trying to stretch area out as far
    833 	 * horizontal as it can go, crossing monitors if possible.
    834 	 *
    835 	 * So, the top-left corner of our box (TL) has the X coordinate of
    836 	 * the right-most strip we started with (the lower), and the Y
    837 	 * coordinate of the top of the area, yielding point (1) above (not
    838 	 * the asterisk; specifically where (1) sits).
    839 	 *
    840 	 * The width W is the difference between the right of the
    841 	 * left-most-ending (in this case, the top) stripe, and the left of
    842 	 * the right-most-starting (the bottom) (plus 1 because math).
    843 	 * That's the width of the intersecting horizontal area (2) above.
    844 	 *
    845 	 * And the height H is just the height of the original area.  And so,
    846 	 * our resulting area is the height of that original area (in ='s),
    847 	 * and stretched to the left and right until it runs into one or the
    848 	 * other monitor edge (1 space to the left, 2 to the right, in our
    849 	 * diagram).
    850 	 */
    851 }
    852 
    853 
    854 /**
    855  * Figure the best way to stretch an area across the full vertical height
    856  * of an RLayout.  This is the backend for the f.xzoom ctwm function,
    857  * zooming a window to the full height of all monitors.
    858  */
    859 RArea
    860 RLayoutFullVert(const RLayout *self, const RArea *area)
    861 {
    862 	int max_y, min_y2;
    863 
    864 	RLayoutFindTopBottomEdges(self, area, &max_y, &min_y2);
    865 
    866 	return RAreaNew(area->x, max_y, area->width, min_y2 - max_y + 1);
    867 
    868 	// X-ref long comment above in RLayoutFullHoriz() for worked example.
    869 	// This is just rotated 90 degrees, but the logic works out about the
    870 	// same.
    871 }
    872 
    873 
    874 /**
    875  * Figure the best way to stretch an area across the largest horizontal
    876  * and vertical space it can from its current position.  Essentially,
    877  * stretch it in all directions until it hits the edge of our available
    878  * space.
    879  *
    880  * This is the backend for the f.xfullzoom function.
    881  */
    882 RArea
    883 RLayoutFull(const RLayout *self, const RArea *area)
    884 {
    885 	RArea full_horiz, full_vert, full1, full2;
    886 
    887 	// Get the boxes for full horizontal and vertical zooms, using the
    888 	// above functions.
    889 	full_horiz = RLayoutFullHoriz(self, area);
    890 	full_vert = RLayoutFullVert(self, area);
    891 
    892 	// Now stretch each of those in the other direction...
    893 	full1 = RLayoutFullVert(self, &full_horiz);
    894 	full2 = RLayoutFullHoriz(self, &full_vert);
    895 
    896 	// And return whichever was bigger.
    897 	return RAreaArea(&full1) > RAreaArea(&full2) ? full1 : full2;
    898 }
    899 
    900 
    901 
    902 /**
    903  * Figure the best way to stretch an area horizontally without crossing
    904  * monitors.
    905  *
    906  * This is the backend for the f.horizoom ctwm function.
    907  */
    908 RArea
    909 RLayoutFullHoriz1(const RLayout *self, const RArea *area)
    910 {
    911 	// Cheat by using RLayoutFull1() to find the RArea for the monitor
    912 	// it's most on.
    913 	RArea target = RLayoutFull1(self, area);
    914 	int max_y, min_y2;
    915 
    916 	// We're stretching horizontally, so the x and width of target (that
    917 	// monitor) are already right.  But we have to figure the y and
    918 	// height...
    919 
    920 	// Generally, the y is the window's original y, unless we had to move
    921 	// it down to get onto the target monitor.  XXX Wait, what if we
    922 	// moved it _up_?
    923 	max_y = max(area->y, target.y);
    924 	target.y = max_y;
    925 
    926 	// The bottom would be the bottom of the area, clipped to the bottom
    927 	// of the monitor.  So the height is the diff.
    928 	min_y2 = min(RAreaY2(area), RAreaY2(&target));
    929 	target.height = min_y2 - max_y + 1;
    930 
    931 	return target;
    932 }
    933 
    934 
    935 /**
    936  * Figure the best way to stretch an area vertically without crossing
    937  * monitors.
    938  *
    939  * This is the backend for the f.zoom ctwm function.
    940  */
    941 RArea
    942 RLayoutFullVert1(const RLayout *self, const RArea *area)
    943 {
    944 	// Let RLayoutFull1() find the right monitor.
    945 	RArea target = RLayoutFull1(self, area);
    946 	int max_x, min_x2;
    947 
    948 	// Stretching vertically, so the y/height of the monitor are already
    949 	// right.
    950 
    951 	// x is where the window was, unless we had to move it right to get
    952 	// onto the monitor.  XXX What if we moved it left?
    953 	max_x = max(area->x, target.x);
    954 	target.x = max_x;
    955 
    956 	// Right side is where it was, unless we have to clip to the monitor.
    957 	min_x2 = min(RAreaX2(area), RAreaX2(&target));
    958 	target.width = min_x2 - max_x + 1;
    959 
    960 	return target;
    961 }
    962 
    963 
    964 /**
    965  * Figure the best way to resize an area to fill one monitor.
    966  *
    967  * This is the backend for the f.fullzoom ctwm function.
    968  *
    969  * \param self  Monitor layout
    970  * \param area  Area (window) to zoom out
    971  */
    972 RArea
    973 RLayoutFull1(const RLayout *self, const RArea *area)
    974 {
    975 	RArea target;
    976 	RAreaList *mit = RAreaListIntersect(self->monitors, area);
    977 	// Start with a list of all the monitors the window is on now.
    978 
    979 	if(mit->len == 0) {
    980 		// Not on any screens.  Find the "nearest" place it would wind
    981 		// up.
    982 		RAreaListFree(mit);
    983 		mit = _RLayoutRecenterHorizontally(self, area);
    984 	}
    985 
    986 	// Of the monitors it's on, find the one that it's "most" on, and
    987 	// return the RArea of it.
    988 	target = RAreaListBestTarget(mit, area);
    989 	RAreaListFree(mit);
    990 	return target;
    991 }
    992 
    993 
    994 
    995 /************************
    996  *
    997  * Finally, some small misc utils.
    998  *
    999  ************************/
   1000 
   1001 
   1002 /**
   1003  * Generate maximal spanning RArea.
   1004  *
   1005  * This is a trivial wrapper of RAreaListBigArea() to hide knowledge of
   1006  * RLayout internals.  Currently only used once; maybe should just be
   1007  * deref'd there...
   1008  */
   1009 RArea
   1010 RLayoutBigArea(const RLayout *self)
   1011 {
   1012 	return RAreaListBigArea(self->monitors);
   1013 }
   1014 
   1015 
   1016 /**
   1017  * How many monitors does a given RLayout contain?
   1018  */
   1019 int
   1020 RLayoutNumMonitors(const RLayout *self)
   1021 {
   1022 	return self->monitors->len;
   1023 }
   1024 
   1025 
   1026 /**
   1027  * Pretty-print an RLayout.
   1028  *
   1029  * Used for dev/debug.
   1030  */
   1031 void
   1032 RLayoutPrint(const RLayout *self)
   1033 {
   1034 	fprintf(stderr, "[monitors=");
   1035 	RAreaListPrint(self->monitors);
   1036 	fprintf(stderr, "\n horiz=");
   1037 	RAreaListPrint(self->horiz);
   1038 	fprintf(stderr, "\n vert=");
   1039 	RAreaListPrint(self->vert);
   1040 	fprintf(stderr, "]\n");
   1041 }
   1042