1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2003-2021 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "ewins.h"
26 #include "hints.h"
27 #include "screen.h"
28 #include "slide.h"
29 
30 #define DEBUG_SIZE 0
31 #if DEBUG_SIZE
32 #define Dprintf Eprintf
33 #else
34 #define Dprintf(fmt...)
35 #endif
36 
37 #define ENABLE_SMART_MAXIMISE	1
38 
39 #define MAX_ABSOLUTE     0	/* Fill screen */
40 #define MAX_AVAILABLE    1	/* Expand until don't cover */
41 #define MAX_CONSERVATIVE 2	/* Expand until something */
42 #define MAX_XINERAMA     3	/* Fill Xinerama screen */
43 #define MAX_HALF_N       4	/* Expand to North half */
44 #define MAX_HALF_S       5	/* Expand to South half */
45 #define MAX_HALF_E       6	/* Expand to East half */
46 #define MAX_HALF_W       7	/* Expand to West half */
47 
48 static int
_ignore(const EWin * ewin,int type)49 _ignore(const EWin * ewin, int type)
50 {
51    if (ewin->state.iconified || EoIsFloating(ewin) ||
52        ewin->props.ignorearrange ||
53        ((type == MAX_AVAILABLE ||
54 	 type == MAX_HALF_N || type == MAX_HALF_S ||
55 	 type == MAX_HALF_E || type == MAX_HALF_W) &&
56 	!ewin->props.never_use_area))
57       return 1;
58    return 0;
59 }
60 
61 static void
_get_span_x(const EWin * ewin,int type,EWin * const * lst,int num,int * px1,int * px2)62 _get_span_x(const EWin * ewin, int type, EWin * const *lst, int num,
63 	    int *px1, int *px2)
64 {
65    int                 i, sx1 = *px1, sx2 = *px2;
66    int                 wx1, wx2, tx1, tx2;
67    EWin               *e;
68 
69    wx1 = EoGetX(ewin);
70    wx2 = wx1 + EoGetW(ewin);
71 
72    for (i = 0; i < num; i++)
73      {
74 	e = lst[i];
75 	if (e == ewin || _ignore(e, type) ||
76 	    !SPANS_COMMON(EoGetY(ewin), EoGetH(ewin), EoGetY(e), EoGetH(e)))
77 	   continue;
78 
79 	tx1 = EoGetX(e);
80 	tx2 = tx1 + EoGetW(e);
81 	if (tx2 <= wx1 && tx2 > sx1)
82 	   sx1 = tx2;
83 	else if (tx1 >= wx2 && tx1 < sx2)
84 	   sx2 = tx1;
85      }
86 
87    *px1 = sx1;
88    *px2 = sx2;
89 }
90 
91 static void
_get_span_y(const EWin * ewin,int type,EWin * const * lst,int num,int * py1,int * py2)92 _get_span_y(const EWin * ewin, int type, EWin * const *lst, int num,
93 	    int *py1, int *py2)
94 {
95    int                 i, sy1 = *py1, sy2 = *py2;
96    int                 wy1, wy2, ty1, ty2;
97    const EWin         *e;
98 
99    wy1 = EoGetY(ewin);
100    wy2 = wy1 + EoGetH(ewin);
101 
102    for (i = 0; i < num; i++)
103      {
104 	e = lst[i];
105 	if (e == ewin || _ignore(e, type) ||
106 	    !SPANS_COMMON(EoGetX(ewin), EoGetW(ewin), EoGetX(e), EoGetW(e)))
107 	   continue;
108 
109 	ty1 = EoGetY(e);
110 	ty2 = ty1 + EoGetH(e);
111 	if (ty2 <= wy1 && ty2 > sy1)
112 	   sy1 = ty2;
113 	else if (ty1 >= wy2 && ty1 < sy2)
114 	   sy2 = ty1;
115      }
116 
117    *py1 = sy1;
118    *py2 = sy2;
119 }
120 
121 #if ENABLE_SMART_MAXIMISE
122 /*
123  * Smart window sizing algorithm.
124  * Based on pareto frontiers and constraint unification.
125  * Blame it on Dan Manjarres.
126  *
127  * The algorithm is to treat the center of the window being maximized as the
128  * center of a cartesian coordinate system, and to find a pareto frontier
129  * in each quadrant of the cartesian plane. The frontier is found from points
130  * that are the corners of other windows or the nearest point on the edge of a
131  * window that spans 2 quadrants. Windows that span more than 2 quadrants are
132  * ignored since they already overlap with the original window.
133  *
134  * The frontiers encode a discrete set of possible window edges, which are sorted
135  * and tested in sequence until the one with the largest area is found.
136  *
137  * Filtering is done to ignore windows that are visually "under"
138  * a window that is overlapped so that hidden windows are not counted as
139  * constraining maximization. Windows that have the never use flag are not
140  * ignored in this manner. Windows with aspect ratio hints are respected, and
141  * placed as close as possible to their original position. All windows may be
142  * slightly shifted to get more area, as the only point that represents the
143  * window is its center, around which the constraints are placed. The new
144  * center may be located anywhere in region of the screen defined by partial
145  * orders of windows along x and y, and conflicts between a windows existing edges
146  * and resizing the window are completely bypassed. In other words you don't have
147  * to grab the mouse to move the window before maximizing it, and you don't need the
148  * mouse to maximizing it either. Alt-Tab and <maximize key command> are all you need.
149  *
150  * computation proceeeds as
151  *
152  * 1) list all windows
153  * 2) filter out windows that overlap maximizing window, and all windows under them.
154  * 3) record the 4 corners of each window in a constraint array
155  * 4) compute the nearest point of any window that crosses the x or y axis and store
156  * 	it as a  constraint.
157  * 5) make 4 copies of the array and adjust for different search directions
158  * 6) for each copy filter in preparation for pareto criteria for each corner of the window
159  * 6a) for upper corners ignore windows that are below window center
160  * 6b) for lower corners ignore windows that are above below window center
161  * 6c) for left  corners ignore windows that are right of window center
162  * 6d) for right corners ignore windows that are left of window center
163  * 7) perform pareto frontier filtering for each quadrant
164  *     after this step the remaining points  are the pareto frontier for each corner
165  *     of the window in isolation, and form a not-too-nonconvex hull topologically equal
166  *     to a circle around the original window's center.
167  *
168  *     Each discrete x value and y value represents a potential location
169  *     for sides of the window, and each window side will touch
170  *     at least one of its potential corner points.
171  *
172  * 8) combine the adjacent corners for each side into arrays holding potential
173  *    window edge locations and maximum spans sorted by distance from center.
174  * 9) for each pair of top and bottom constraints find the available area from
175  *    the left and right constraints.
176  * 10) adjust for aspect ratio.
177  * 11) pick the biggest one
178  * 12) and on the seventh day rest
179  * */
180 
181 typedef struct {
182    int                 dx;
183    int                 dy;
184    int                 dominated;
185 } point;
186 
187 static int
_gti(const void * p1,const void * p2)188 _gti(const void *p1, const void *p2)
189 {
190    return (*(int *)p1) - *((int *)p2);
191 }
192 
193 static int
_lti(const void * p1,const void * p2)194 _lti(const void *p1, const void *p2)
195 {
196    return (*(int *)p2) - *((int *)p1);
197 }
198 
199 static int
_gtx(const void * p1,const void * p2)200 _gtx(const void *p1, const void *p2)
201 {
202    return ((point *) p1)->dx - ((point *) p2)->dx;
203 }
204 
205 static int
_ltx(const void * p1,const void * p2)206 _ltx(const void *p1, const void *p2)
207 {
208    return ((point *) p2)->dx - ((point *) p1)->dx;
209 }
210 
211 #if 0
212 static int
213 _gty(const void *p1, const void *p2)
214 {
215    return ((point *) p1)->dy - ((point *) p2)->dy;
216 }
217 
218 static int
219 _lty(const void *p1, const void *p2)
220 {
221    return ((point *) p2)->dy - ((point *) p1)->dy;
222 }
223 #endif
224 
225 static void
sort_ints(int * is,int n,int ascending)226 sort_ints(int *is, int n, int ascending)
227 {
228    if (ascending >= 0)
229       qsort(is, n, sizeof(int), _gti);
230    else
231       qsort(is, n, sizeof(int), _lti);
232 }
233 
234 static void
sort_points_x(point * ps,int n,int ascending)235 sort_points_x(point * ps, int n, int ascending)
236 {
237    if (ascending >= 0)
238       qsort(ps, n, sizeof(point), _gtx);
239    else
240       qsort(ps, n, sizeof(point), _ltx);
241 }
242 
243 #if 0
244 static void
245 sort_points_y(point * ps, int n, int ascending)
246 {
247    if (ascending >= 0)
248       qsort(ps, n, sizeof(point), _gty);
249    else
250       qsort(ps, n, sizeof(point), _lty);
251 }
252 #endif
253 
254 static void
uniq_ints(int * is,int * n)255 uniq_ints(int *is, int *n)
256 {
257    int                 i, j;
258 
259    for (i = 0, j = 0; i < *n - 1; i++)
260      {
261 	if (is[i] != is[i + 1])
262 	  {
263 	     is[j] = is[i];
264 	     j++;
265 	  }
266      }
267    is[j] = is[i];
268    j++;
269    *n = j;
270 }
271 
272 static void
filter_points(point * p,int * np,int max_dx,int max_dy)273 filter_points(point * p, int *np, int max_dx, int max_dy)
274 {
275    int                 i, j, n = *np;
276 
277    /* this is step 6 from above */
278    for (i = 0; i < n; i++)
279      {
280 	if (p[i].dx < 0)
281 	   p[i].dominated = 1;
282 	if (p[i].dy < 0)
283 	   p[i].dominated = 1;
284 	/* clip to screen */
285 	if (p[i].dx > max_dx)
286 	   p[i].dx = max_dx;
287 	if (p[i].dy > max_dy)
288 	   p[i].dy = max_dy;
289      }
290 
291    /* this is step 7 from above */
292    for (i = 0; i < n; i++)
293      {
294 	if (p[i].dominated)
295 	   continue;
296 
297 	for (j = 0; j < n; j++)
298 	  {
299 	     if (i == j)
300 		continue;	/* self dominance = no */
301 	     if ((p[i].dx <= p[j].dx) && (p[i].dy <= p[j].dy))
302 	       {
303 		  p[j].dominated = 1;
304 	       }
305 	  }
306      }
307 
308    /* actual pareto frontier filtering */
309    for (i = 0, j = 0; i < n; i++)
310      {
311 	if (!p[i].dominated)
312 	  {
313 	     p[j] = p[i];
314 	     j++;
315 	  }
316      }
317 
318    *np = j;
319 }
320 
321 #define _get_span_xy pareto_maximizer
322 static void
pareto_maximizer(EWin * ewin,int type,EWin * const * lst,int num,int * avail_x1,int * avail_x2,int * avail_y1,int * avail_y2)323 pareto_maximizer(EWin * ewin, int type, EWin * const *lst, int num,
324 		 int *avail_x1, int *avail_x2, int *avail_y1, int *avail_y2)
325 {
326    int                 x, y, w, h;
327    int                 cx, cy;	/* center */
328    int                 i, j, k;
329    point              *constraints_tr = NULL;	/* top right   */
330    point              *constraints_tl = NULL;	/* top left    */
331    point              *constraints_br = NULL;	/* botom right */
332    point              *constraints_bl = NULL;	/* botom left  */
333    int                 num_tr, num_tl, num_br, num_bl;
334    int                *top_ds = NULL;
335    int                *bottom_ds = NULL;
336    point              *left_ds = NULL;
337    point              *right_ds = NULL;
338    int                *tc, *bc;
339    point              *lc, *rc;	/* temp cursors */
340    int                 num_t, num_b, num_l, num_r;
341    float               aspect;
342    EWin              **filtered_lst, **stacked_lst, *pe, *pe2;
343    char               *done_stacking_flag;
344    char               *stacked_above_flag;
345    int                 num_stacked, stacked_above;
346    int                 area, new_area;
347    int                 recenter, new_recenter;
348    int                 td, bd, ld, rd;	/* displacement to maximized edges */
349 
350    Dprintf("searching within %d-%d %d-%d\n",
351 	   *avail_x1, *avail_x2, *avail_y1, *avail_y2);
352    x = EoGetX(ewin);
353    y = EoGetY(ewin);
354    h = EoGetH(ewin);
355    w = EoGetW(ewin);
356    cx = x + w / 2;
357    cy = y + h / 2;
358 
359    /* center must be within available region */
360    cx = (cx >= *avail_x2) ? *avail_x2 - 1 : cx;
361    cy = (cy >= *avail_y2) ? *avail_y2 - 1 : cy;
362    cx = (cx < *avail_x1) ? *avail_x1 : cx;
363    cy = (cy < *avail_y1) ? *avail_y1 : cy;
364 
365    filtered_lst = EMALLOC(EWin *, num);
366    stacked_lst = EMALLOC(EWin *, num + 1);
367    stacked_above_flag = ECALLOC(char, num);
368    done_stacking_flag = ECALLOC(char, num);
369 
370    if (!filtered_lst || !stacked_lst || !done_stacking_flag ||
371        !stacked_above_flag)
372       goto freedom;
373 
374    stacked_lst[0] = ewin;
375    num_stacked = 1;
376    /* ignore windows already overlapping ours and any windows UNDER them */
377 
378    /* start by detecting windows we overlap */
379    for (i = 0, stacked_above = 0; i < num; i++)
380      {
381 	pe = lst[i];
382 
383 	if (pe == ewin)
384 	  {
385 	     stacked_above = 1;
386 	  }
387 	if ((pe == ewin) || _ignore(pe, type))
388 	  {
389 	     done_stacking_flag[i] = 1;
390 	     Dprintf("ignoring #%d %s\n", i, EwinGetTitle(pe));
391 	     continue;
392 	  }
393 
394 	if (SPANS_COMMON(x + 1, w - 2, EoGetX(pe), EoGetW(pe)) &&
395 	    SPANS_COMMON(y + 1, h - 2, EoGetY(pe), EoGetH(pe)))
396 	  {
397 	     stacked_lst[num_stacked] = pe;
398 	     stacked_above_flag[num_stacked] = stacked_above;
399 	     num_stacked++;
400 	     done_stacking_flag[i] = 1;
401 	     Dprintf("overlap #%d %s\n", i, EwinGetTitle(pe));
402 	  }
403 	else
404 	  {
405 	     Dprintf("do not overlap #%d %s\n", i, EwinGetTitle(pe));
406 	  }
407      }
408 
409    /* extend the stacked list to windows that are UNDER other items in the stacked list */
410    for (i = 1; i < num_stacked; i++)
411      {
412 	int                 sx, sy, sw, sh;
413 
414 	pe2 = stacked_lst[i];
415 	sx = EoGetX(pe2);
416 	sy = EoGetY(pe2);
417 	sh = EoGetH(pe2);
418 	sw = EoGetW(pe2);
419 
420 	/* find stacked_lst window pe2 in the general window list */
421 	for (j = 0; j < num; j++)
422 	  {
423 	     pe = lst[j];
424 	     if (pe == pe2)
425 		break;
426 	  }
427 	if (stacked_above_flag[i])
428 	  {
429 	     Dprintf("metaoverlap testing from understacked %s\n",
430 		     EwinGetTitle(pe));
431 	  }
432 	else
433 	  {
434 	     Dprintf("NOT metaoverlap testing from overstacked %s\n",
435 		     EwinGetTitle(pe));
436 	     continue;
437 	  }
438 
439 	for (; j < num; j++)
440 	  {
441 	     pe = lst[j];
442 
443 	     if (done_stacking_flag[j])
444 		continue;
445 	     if (pe->props.never_use_area)
446 	       {
447 		  Dprintf("not using area %s\n", EwinGetTitle(pe));
448 		  continue;
449 	       }
450 
451 	     if (SPANS_COMMON(sx + 1, sw - 2, EoGetX(pe), EoGetW(pe)) &&
452 		 SPANS_COMMON(sy + 1, sh - 2, EoGetY(pe), EoGetH(pe)))
453 	       {
454 		  /* the list is already top down so if it overlaps it's also under */
455 		  /* we hope! */
456 		  stacked_lst[num_stacked] = pe;
457 		  stacked_above_flag[num_stacked] = 1;
458 		  num_stacked++;
459 		  done_stacking_flag[j] = 1;
460 		  Dprintf("metaoverlap #%d %s\n", j, EwinGetTitle(pe));
461 	       }
462 	     else
463 	       {
464 		  Dprintf("no metaoverlap #%d %s\n", j, EwinGetTitle(pe));
465 	       }
466 	  }
467      }
468 
469    /* copy remaining windows to our working set */
470    for (i = 0, j = 0; i < num; i++)
471      {
472 	pe = lst[i];
473 
474 	if (done_stacking_flag[i])
475 	  {
476 	     Dprintf("no stacked constraint from #%d %s\n", i,
477 		     EwinGetTitle(pe));
478 	     continue;
479 	  }
480 
481 	if ((pe == ewin) || _ignore(pe, type) ||
482 	    /* ignore windws that do not overlap with current search area */
483 	    !(SPANS_COMMON(*avail_x1, *avail_x2 - *avail_x1,
484 			   EoGetX(pe), EoGetW(pe)) &&
485 	      SPANS_COMMON(*avail_y1, *avail_y2 - *avail_y1,
486 			   EoGetY(pe), EoGetH(pe))))
487 	  {
488 	     Dprintf("no constraint from %s\n", EwinGetTitle(pe));
489 	     continue;
490 	  }
491 	Dprintf("constraint from %s\n", EwinGetTitle(pe));
492 	filtered_lst[j] = pe;
493 	j++;
494      }
495 
496    /* allocate memory to hold constraints */
497    num = j + 1;
498    num_tl = num_bl = num_br = num_tr = num * 5;
499    constraints_tl = EMALLOC(point, num * 5);
500    constraints_tr = EMALLOC(point, num * 5);
501    constraints_bl = EMALLOC(point, num * 5);
502    constraints_br = ECALLOC(point, num * 5);
503 
504    if (!constraints_tl || !constraints_tr || !constraints_bl || !constraints_br)
505       goto freedom;
506 
507    for (i = 1; i < num; i++)
508      {
509 	pe = filtered_lst[i - 1];
510 	/* store window corners as candidate constraints */
511 	constraints_br[5 * i + 0].dx = EoGetX(pe);
512 	constraints_br[5 * i + 0].dy = EoGetY(pe);
513 	constraints_br[5 * i + 1].dx = EoGetX(pe) + EoGetW(pe);
514 	constraints_br[5 * i + 1].dy = EoGetY(pe);
515 	constraints_br[5 * i + 2].dx = EoGetX(pe) + EoGetW(pe);
516 	constraints_br[5 * i + 2].dy = EoGetY(pe) + EoGetH(pe);
517 	constraints_br[5 * i + 3].dx = EoGetX(pe);
518 	constraints_br[5 * i + 3].dy = EoGetY(pe) + EoGetH(pe);
519 
520 	/* if window occupies 2 quadrants add a constraint for the
521 	 * intersection of the x or y axis and the window */
522 	if (SPANS_COMMON(EoGetX(pe), EoGetW(pe), cx, 1))
523 	  {
524 	     Dprintf("got horiz edge contraint\n");
525 	     constraints_br[5 * i + 4].dx = cx;
526 	     if (EoGetY(pe) > cy)
527 	       {
528 		  constraints_br[5 * i + 4].dy = EoGetY(pe);
529 	       }
530 	     else
531 	       {
532 		  constraints_br[5 * i + 4].dy = EoGetY(pe) + EoGetH(pe);
533 	       }
534 	  }
535 	else if (SPANS_COMMON(EoGetY(pe), EoGetH(pe), cy, 1))
536 	  {
537 	     Dprintf("got ver edge contraint\n");
538 	     constraints_br[5 * i + 4].dy = cy;
539 	     if (EoGetX(pe) > cx)
540 	       {
541 		  constraints_br[5 * i + 4].dx = EoGetX(pe);
542 	       }
543 	     else
544 	       {
545 		  constraints_br[5 * i + 4].dx = EoGetX(pe) + EoGetW(pe);
546 	       }
547 	  }
548 	else
549 	  {
550 	     constraints_br[5 * i + 4].dominated = 1;
551 	     Dprintf("got no edge constraint from win #%d %s\n", i - 1,
552 		     EwinGetTitle(pe));
553 	  }
554      }
555 
556    /* add constraints to keep the window on-screen */
557    constraints_br[0].dx = cx;
558    constraints_br[0].dy = *avail_y1 - 1;
559    constraints_br[1].dx = cx;
560    constraints_br[1].dy = *avail_y2 + 1;
561    constraints_br[2].dx = *avail_x1 - 1;
562    constraints_br[2].dy = cy;
563    constraints_br[3].dx = *avail_x2 + 1;
564    constraints_br[3].dy = cy;
565    constraints_br[4].dominated = 1;
566 
567    /* subtract out center to get distance to constraints */
568    for (i = 0; i < num_tr; i++)
569      {
570 	constraints_br[i].dx -= cx;
571 	constraints_br[i].dy -= cy;
572      }
573 
574    /* make 4 copies: one for each corner to optimize */
575    memcpy(constraints_tl, constraints_br, sizeof(point) * num_tl);
576    memcpy(constraints_bl, constraints_br, sizeof(point) * num_tl);
577    memcpy(constraints_tr, constraints_br, sizeof(point) * num_tl);
578 
579    for (i = 0; i < num_tr; i++)
580      {
581 	/* correct displacements to be positive in the direction of expansion */
582 	constraints_tl[i].dx *= -1;
583 	constraints_tl[i].dy *= -1;
584 
585 	constraints_bl[i].dx *= -1;
586 
587 	constraints_tr[i].dy *= -1;
588      }
589 
590    /* bust out that pareto frontier */
591    filter_points(constraints_tl, &num_tl, cx - *avail_x1, cy - *avail_y1);
592    filter_points(constraints_tr, &num_tr, *avail_x2 - cx, cy - *avail_y1);
593    filter_points(constraints_bl, &num_bl, cx - *avail_x1, *avail_y2 - cy);
594    filter_points(constraints_br, &num_br, *avail_x2 - cx, *avail_y2 - cy);
595 
596    /* now need to convert corner constraints to candidate edge constraints */
597    num_t = num_tl + num_tr;
598    num_b = num_bl + num_br;
599    num_l = num_tl + num_bl;
600    num_r = num_tr + num_br;
601    tc = top_ds = EMALLOC(int, num_t);
602    bc = bottom_ds = EMALLOC(int, num_b);
603 
604    lc = left_ds = EMALLOC(point, num_l);
605    rc = right_ds = EMALLOC(point, num_r);
606 
607    if (!tc || !bc || !lc || !rc)
608       goto freedom;
609 
610    /* convert constraint distances back to to constraint displacements
611     * using cursor pointers to accumulate constraints for each edge */
612    for (i = 0; i < num_tr; i++)
613      {
614 	rc->dx = constraints_tr[i].dx;
615 	rc->dy = -constraints_tr[i].dy;
616 	*tc = -constraints_tr[i].dy;
617 	tc++;
618 	rc++;
619      }
620    for (i = 0; i < num_tl; i++)
621      {
622 	lc->dx = -constraints_tl[i].dx;
623 	lc->dy = -constraints_tl[i].dy;
624 	*tc = -constraints_tl[i].dy;
625 	tc++;
626 	lc++;
627      }
628    for (i = 0; i < num_br; i++)
629      {
630 	rc->dx = constraints_br[i].dx;
631 	rc->dy = constraints_br[i].dy;
632 	*bc = constraints_br[i].dy;
633 	bc++;
634 	rc++;
635      }
636    for (i = 0; i < num_bl; i++)
637      {
638 	lc->dx = -constraints_bl[i].dx;
639 	lc->dy = constraints_bl[i].dy;
640 	*bc = constraints_bl[i].dy;
641 	bc++;
642 	lc++;
643      }
644 
645    /* sort the lists for easy searching */
646    sort_points_x(left_ds, num_l, +1);
647    sort_points_x(right_ds, num_r, -1);
648    sort_ints(top_ds, num_t, +1);
649    sort_ints(bottom_ds, num_b, -1);
650    uniq_ints(top_ds, &num_t);
651    uniq_ints(bottom_ds, &num_b);
652 
653    /* brute force test the combinitorics:
654     * for each pair of possible top and bottom displacements,
655     * find the best possible pair of left and right displacements */
656 
657    area = 0;
658    recenter = 0;
659 
660    for (i = 0; i < num_t; i++)	/* top indices */
661      {
662 	for (j = 0; j < num_b; j++)	/* bottom indices */
663 	  {
664 	     int                 new_w, new_h, trim;
665 
666 	     td = top_ds[i];	/* displacement to top */
667 	     bd = bottom_ds[j];	/* displacement to bottom */
668 	     Dprintf("starting search in td-bd %d-%d\n", td, bd);
669 	     ld = left_ds[0].dx;
670 	     rd = right_ds[0].dx;
671 	     Dprintf("search in y from %d to %d\n", td, bd);
672 
673 	     /* find furthest left given top, bottom */
674 	     for (k = 0; k < num_l; k++)
675 	       {
676 		  if (left_ds[k].dy <= td)	/* constraint point is above top, skip it */
677 		    {
678 		       Dprintf("left ignoring above point %d,%d\n",
679 			       left_ds[k].dx, left_ds[k].dy);
680 		       continue;
681 		    }
682 		  if (left_ds[k].dy >= bd)	/* constraint point is below bottom, skip it */
683 		    {
684 		       Dprintf("left ignoring below point %d,%d\n",
685 			       left_ds[k].dx, left_ds[k].dy);
686 		       continue;
687 		    }
688 		  Dprintf("left using point %d,%d\n", left_ds[k].dx,
689 			  left_ds[k].dy);
690 		  if (left_ds[k].dx > ld)
691 		     ld = left_ds[k].dx + 1;
692 	       }
693 
694 	     /* find furthest right given top, bottom */
695 	     for (k = 0; k < num_r; k++)
696 	       {
697 		  if (right_ds[k].dy <= td)	/* constraint point is above top, skip it */
698 		    {
699 		       Dprintf("right ignoring above point %d,%d\n",
700 			       right_ds[k].dx, right_ds[k].dy);
701 		       continue;
702 		    }
703 		  if (right_ds[k].dy >= bd)	/* constraint point is below bottom, skip it */
704 		    {
705 		       Dprintf("right ignoring below point %d,%d\n",
706 			       right_ds[k].dx, right_ds[k].dy);
707 		       continue;
708 		    }
709 		  Dprintf("right using point %d,%d\n", right_ds[k].dx,
710 			  right_ds[k].dy);
711 		  if (right_ds[k].dx < rd)
712 		     rd = right_ds[k].dx - 1;
713 	       }
714 
715 	     /* almost there..... need to correct for aspect ratio
716 	      * and keep center as close as possible to old center
717 	      * in case of duplicates...... */
718 
719 	     new_w = 1 - ld + rd;
720 	     new_h = 1 - td + bd;
721 	     aspect = (float)new_w / new_h;
722 	     new_area = new_w * new_h;
723 
724 	     if (new_area > area)
725 	       {
726 		  if (aspect > ewin->icccm.aspect_max)
727 		    {
728 		       do
729 			 {
730 			    if (abs(ld) < abs(rd))
731 			      {
732 				 Dprintf("trimming right for aspect\n");
733 				 rd--;
734 			      }
735 			    else
736 			      {
737 				 Dprintf("trimming left for aspect\n");
738 				 ld++;
739 			      }
740 			    new_w = 1 - ld + rd;
741 			    trim =
742 			       new_w - (int)(new_h * ewin->icccm.aspect_min);
743 			 }
744 		       while (trim > 0);
745 		       Dprintf("ld now %d, rd now %d\n", ld, rd);
746 		       Dprintf("td now %d, bd now %d\n", td, bd);
747 		       Dprintf("\n");
748 		    }
749 		  else if (aspect < ewin->icccm.aspect_min)
750 		    {
751 		       do
752 			 {
753 			    if (abs(td) < abs(bd))
754 			      {
755 				 Dprintf("trimming bottom for aspect\n");
756 				 bd--;
757 			      }
758 			    else
759 			      {
760 				 Dprintf("trimming top for aspect\n");
761 				 td++;
762 			      }
763 			    new_h = 1 - td + bd;
764 			    trim =
765 			       new_h - (int)(new_w / ewin->icccm.aspect_min);
766 			 }
767 		       while (trim > 0);
768 		       Dprintf("ld now %d, rd now %d\n", ld, rd);
769 		       Dprintf("td now %d, bd now %d\n", td, bd);
770 		       Dprintf("\n");
771 		    }
772 
773 		  new_area = new_w * new_h;
774 		  new_recenter = abs(td) + abs(bd) + abs(ld) + abs(rd);
775 
776 		  if ((new_area > area) ||
777 		      ((new_area == area) && (new_recenter < recenter)))
778 		    {
779 		       Dprintf("new area %ld old area %ld\n", new_area, area);
780 		       Dprintf("%d-%d x %d-%d\n", x, x + w, y, y + h);
781 		       area = new_area;
782 		       recenter = new_recenter;
783 		       x = cx + ld;
784 		       y = cy + td;
785 		       w = (1 - ld + rd);
786 		       h = (1 - td + bd);
787 		    }
788 	       }
789 	     Dprintf("===========================\n");
790 	  }
791      }
792 
793    *avail_x1 = x;
794    *avail_x2 = x + w;
795    *avail_y1 = y;
796    *avail_y2 = y + h;
797 
798    /* rest a while */
799 
800  freedom:
801    Efree(top_ds);
802    Efree(bottom_ds);
803    Efree(left_ds);
804    Efree(right_ds);
805    Efree(constraints_tl);
806    Efree(constraints_tr);
807    Efree(constraints_bl);
808    Efree(constraints_br);
809    Efree(filtered_lst);
810    Efree(stacked_lst);
811    Efree(done_stacking_flag);
812    Efree(stacked_above_flag);
813 }
814 #endif /* ENABLE_SMART_MAXIMISE */
815 
816 void
MaxSizeHV(EWin * ewin,const char * resize_type,int hor,int ver,int flags)817 MaxSizeHV(EWin * ewin, const char *resize_type, int hor, int ver, int flags)
818 {
819    int                 x, y, w, h, x1, x2, y1, y2, type, bl, br, bt, bb;
820    EWin               *const *lst;
821    int                 num, speed;
822    int                 old_hor = ewin->state.maximized_horz != 0;
823    int                 old_ver = ewin->state.maximized_vert != 0;
824 
825    if (!ewin)
826       return;
827 
828    type = MAX_ABSOLUTE;		/* Select default */
829    if (!resize_type || !resize_type[0])
830       type = Conf.movres.mode_maximize_default;
831    else if (!strcmp(resize_type, "absolute"))
832       type = MAX_ABSOLUTE;
833    else if (!strcmp(resize_type, "available"))
834       type = MAX_AVAILABLE;
835    else if (!strcmp(resize_type, "conservative"))
836       type = MAX_CONSERVATIVE;
837    else if (!strcmp(resize_type, "xinerama"))
838       type = MAX_XINERAMA;
839    else if (!strcmp(resize_type, "half_N"))
840       type = MAX_HALF_N;
841    else if (!strcmp(resize_type, "half_S"))
842       type = MAX_HALF_S;
843    else if (!strcmp(resize_type, "half_E"))
844       type = MAX_HALF_E;
845    else if (!strcmp(resize_type, "half_W"))
846       type = MAX_HALF_W;
847 
848    if (ewin->state.inhibit_max_hor && hor)
849       return;
850    if (ewin->state.inhibit_max_ver && ver)
851       return;
852 
853    if ((type == MAX_HALF_E || type == MAX_HALF_W) && hor && !ver)
854       old_ver = 0;
855    if ((type == MAX_HALF_N || type == MAX_HALF_S) && ver && !hor)
856       old_hor = 0;
857 
858    if (!old_hor && !old_ver)
859      {
860 	ewin->save_max.x = EoGetX(ewin);
861 	ewin->save_max.y = EoGetY(ewin);
862 	ewin->save_max.w = ewin->client.w;
863 	ewin->save_max.h = ewin->client.h;
864      }
865 
866    /* Figure out target state */
867    if (hor && ver)
868      {
869 	hor = ver = ((old_hor && old_ver) ? 0 : 1);
870      }
871    else
872      {
873 	hor = (hor) ? !old_hor : old_hor;
874 	ver = (ver) ? !old_ver : old_ver;
875      }
876 
877    ewin->state.maximized_horz = hor;
878    ewin->state.maximized_vert = ver;
879 
880    Dprintf("h/v old = %d/%d new=%d/%d\n", old_hor, old_ver, hor, ver);
881    if (!hor && !ver)
882      {
883 	/* Restore regular state */
884 	x = ewin->save_max.x;
885 	y = ewin->save_max.y;
886 	w = ewin->save_max.w;
887 	h = ewin->save_max.h;
888 	goto do_resize;
889      }
890    if (old_ver == ver && old_hor && !hor)
891      {
892 	/* Turn off horizontal maxsize */
893 	x = ewin->save_max.x;
894 	y = EoGetY(ewin);
895 	w = ewin->save_max.w;
896 	h = ewin->client.h;
897 	goto do_resize;
898      }
899    if (old_hor == hor && old_ver && !ver)
900      {
901 	/* Turn off vertical maxsize */
902 	x = EoGetX(ewin);
903 	y = ewin->save_max.y;
904 	w = ewin->client.w;
905 	h = ewin->save_max.h;
906 	goto do_resize;
907      }
908 
909    /* Default is no change */
910    x = EoGetX(ewin);
911    y = EoGetY(ewin);
912    h = EoGetH(ewin);
913    w = EoGetW(ewin);
914 
915    switch (type)
916      {
917      case MAX_XINERAMA:
918 	if (hor)
919 	  {
920 	     x = 0;
921 	     w = WinGetW(VROOT);
922 	  }
923 	if (ver)
924 	  {
925 	     y = 0;
926 	     h = WinGetH(VROOT);
927 	  }
928 	break;
929 
930      default:
931      case MAX_ABSOLUTE:
932      case MAX_AVAILABLE:
933      case MAX_CONSERVATIVE:
934      case MAX_HALF_N:
935      case MAX_HALF_S:
936      case MAX_HALF_E:
937      case MAX_HALF_W:
938 	ScreenGetAvailableArea(x + w / 2, y + h / 2, &x1, &y1, &x2, &y2,
939 			       Conf.place.ignore_struts_maximize);
940 	x2 += x1;
941 	y2 += y1;
942 
943 	if (Conf.movres.dragbar_nocover && type != MAX_ABSOLUTE)
944 	  {
945 	     /* Leave room for the dragbar */
946 	     switch (Conf.desks.dragdir)
947 	       {
948 	       case 0:		/* left */
949 		  if (x1 < Conf.desks.dragbar_width)
950 		     x1 = Conf.desks.dragbar_width;
951 		  break;
952 
953 	       case 1:		/* right */
954 		  if (x2 > WinGetW(VROOT) - Conf.desks.dragbar_width)
955 		     x2 = WinGetW(VROOT) - Conf.desks.dragbar_width;
956 		  break;
957 
958 	       case 2:		/* top */
959 		  if (y1 < Conf.desks.dragbar_width)
960 		     y1 = Conf.desks.dragbar_width;
961 		  break;
962 
963 	       case 3:		/* bottom */
964 		  if (y2 > WinGetH(VROOT) - Conf.desks.dragbar_width)
965 		     y2 = WinGetH(VROOT) - Conf.desks.dragbar_width;
966 		  break;
967 
968 	       default:
969 		  break;
970 	       }
971 	  }
972 
973 	if (type == MAX_ABSOLUTE)
974 	  {
975 	     /* Simply ignore all windows */
976 	     lst = NULL;
977 	     num = 0;
978 	  }
979 	else
980 	  {
981 	     lst = EwinListGetForDesk(&num, EoGetDesk(ewin));
982 	  }
983 
984 	if (type == MAX_HALF_E && hor)
985 	  {
986 	     x1 += (x2 - x1) / 2;
987 	  }
988 	if (type == MAX_HALF_W && hor)
989 	  {
990 	     x2 -= (x2 - x1) / 2;
991 	  }
992 	if (type == MAX_HALF_N && ver)
993 	  {
994 	     y2 -= (y2 - y1) / 2;
995 	  }
996 	if (type == MAX_HALF_S && ver)
997 	  {
998 	     y1 += (y2 - y1) / 2;
999 	  }
1000 
1001 #if ENABLE_SMART_MAXIMISE
1002 	if (type == MAX_CONSERVATIVE && ver && hor &&
1003 	    ( /*(!old_hor && !old_ver) || */ Conf.movres.enable_smart_max_hv))
1004 	  {
1005 	     _get_span_xy(ewin, type, lst, num, &x1, &x2, &y1, &y2);
1006 	     x = x1;
1007 	     w = x2 - x1;
1008 	     y = y1;
1009 	     h = y2 - y1;
1010 	     break;
1011 	  }
1012 #endif
1013 
1014 	if (ver)
1015 	  {
1016 	     _get_span_y(ewin, type, lst, num, &y1, &y2);
1017 	     y = y1;
1018 	     h = y2 - y1;
1019 	  }
1020 
1021 	if (hor)
1022 	  {
1023 	     _get_span_x(ewin, type, lst, num, &x1, &x2);
1024 	     x = x1;
1025 	     w = x2 - x1;
1026 	  }
1027 
1028 	break;
1029      }
1030 
1031    EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
1032    w -= (bl + br);
1033    if (w < 10)
1034       w = 10;
1035    h -= (bt + bb);
1036    if (h < 10)
1037       h = 10;
1038 
1039  do_resize:
1040    speed = Conf.movres.maximize_animate ? Conf.movres.maximize_speed : 0;
1041    EwinSlideSizeTo(ewin, x, y, w, h, speed, 0, flags | SLIDE_WARP);
1042 
1043    HintsSetWindowState(ewin);
1044 }
1045