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