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