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