e16/src/size.c

1049 lines
32 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2003-2023 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "E.h"
#include "ewins.h"
#include "hints.h"
#include "screen.h"
#include "slide.h"
#define DEBUG_SIZE 0
#if DEBUG_SIZE
#define Dprintf Eprintf
#else
#define Dprintf(fmt...)
#endif
#define ENABLE_SMART_MAXIMISE 1
#define MAX_ABSOLUTE 0 /* Fill screen */
#define MAX_AVAILABLE 1 /* Expand until don't cover */
#define MAX_CONSERVATIVE 2 /* Expand until something */
#define MAX_XINERAMA 3 /* Fill Xinerama screen */
#define MAX_HALF_N 4 /* Expand to North half */
#define MAX_HALF_S 5 /* Expand to South half */
#define MAX_HALF_E 6 /* Expand to East half */
#define MAX_HALF_W 7 /* Expand to West half */
static int
_ignore(const EWin *ewin, int type)
{
if (ewin->state.iconified || EoIsFloating(ewin) ||
ewin->props.ignorearrange ||
((type == MAX_AVAILABLE ||
type == MAX_HALF_N || type == MAX_HALF_S ||
type == MAX_HALF_E || type == MAX_HALF_W) &&
!ewin->props.never_use_area))
return 1;
return 0;
}
static void
_get_span_x(const EWin *ewin, int type, EWin *const *lst, int num,
int *px1, int *px2)
{
int i, sx1 = *px1, sx2 = *px2;
int wx1, wx2, tx1, tx2;
EWin *e;
wx1 = EoGetX(ewin);
wx2 = wx1 + EoGetW(ewin);
for (i = 0; i < num; i++)
{
e = lst[i];
if (e == ewin || _ignore(e, type) ||
!SPANS_COMMON(EoGetY(ewin), EoGetH(ewin), EoGetY(e), EoGetH(e)))
continue;
tx1 = EoGetX(e);
tx2 = tx1 + EoGetW(e);
if (tx2 <= wx1 && tx2 > sx1)
sx1 = tx2;
else if (tx1 >= wx2 && tx1 < sx2)
sx2 = tx1;
}
*px1 = sx1;
*px2 = sx2;
}
static void
_get_span_y(const EWin *ewin, int type, EWin *const *lst, int num,
int *py1, int *py2)
{
int i, sy1 = *py1, sy2 = *py2;
int wy1, wy2, ty1, ty2;
const EWin *e;
wy1 = EoGetY(ewin);
wy2 = wy1 + EoGetH(ewin);
for (i = 0; i < num; i++)
{
e = lst[i];
if (e == ewin || _ignore(e, type) ||
!SPANS_COMMON(EoGetX(ewin), EoGetW(ewin), EoGetX(e), EoGetW(e)))
continue;
ty1 = EoGetY(e);
ty2 = ty1 + EoGetH(e);
if (ty2 <= wy1 && ty2 > sy1)
sy1 = ty2;
else if (ty1 >= wy2 && ty1 < sy2)
sy2 = ty1;
}
*py1 = sy1;
*py2 = sy2;
}
#if ENABLE_SMART_MAXIMISE
/*
* Smart window sizing algorithm.
* Based on pareto frontiers and constraint unification.
* Blame it on Dan Manjarres.
*
* The algorithm is to treat the center of the window being maximized as the
* center of a cartesian coordinate system, and to find a pareto frontier
* in each quadrant of the cartesian plane. The frontier is found from points
* that are the corners of other windows or the nearest point on the edge of a
* window that spans 2 quadrants. Windows that span more than 2 quadrants are
* ignored since they already overlap with the original window.
*
* The frontiers encode a discrete set of possible window edges, which are sorted
* and tested in sequence until the one with the largest area is found.
*
* Filtering is done to ignore windows that are visually "under"
* a window that is overlapped so that hidden windows are not counted as
* constraining maximization. Windows that have the never use flag are not
* ignored in this manner. Windows with aspect ratio hints are respected, and
* placed as close as possible to their original position. All windows may be
* slightly shifted to get more area, as the only point that represents the
* window is its center, around which the constraints are placed. The new
* center may be located anywhere in region of the screen defined by partial
* orders of windows along x and y, and conflicts between a windows existing edges
* and resizing the window are completely bypassed. In other words you don't have
* to grab the mouse to move the window before maximizing it, and you don't need the
* mouse to maximizing it either. Alt-Tab and <maximize key command> are all you need.
*
* computation proceeeds as
*
* 1) list all windows
* 2) filter out windows that overlap maximizing window, and all windows under them.
* 3) record the 4 corners of each window in a constraint array
* 4) compute the nearest point of any window that crosses the x or y axis and store
* it as a constraint.
* 5) make 4 copies of the array and adjust for different search directions
* 6) for each copy filter in preparation for pareto criteria for each corner of the window
* 6a) for upper corners ignore windows that are below window center
* 6b) for lower corners ignore windows that are above below window center
* 6c) for left corners ignore windows that are right of window center
* 6d) for right corners ignore windows that are left of window center
* 7) perform pareto frontier filtering for each quadrant
* after this step the remaining points are the pareto frontier for each corner
* of the window in isolation, and form a not-too-nonconvex hull topologically equal
* to a circle around the original window's center.
*
* Each discrete x value and y value represents a potential location
* for sides of the window, and each window side will touch
* at least one of its potential corner points.
*
* 8) combine the adjacent corners for each side into arrays holding potential
* window edge locations and maximum spans sorted by distance from center.
* 9) for each pair of top and bottom constraints find the available area from
* the left and right constraints.
* 10) adjust for aspect ratio.
* 11) pick the biggest one
* 12) and on the seventh day rest
* */
typedef struct {
int dx;
int dy;
int dominated;
} point;
static int
_gti(const void *p1, const void *p2)
{
return (*(int *)p1) - *((int *)p2);
}
static int
_lti(const void *p1, const void *p2)
{
return (*(int *)p2) - *((int *)p1);
}
static int
_gtx(const void *p1, const void *p2)
{
return ((point *) p1)->dx - ((point *) p2)->dx;
}
static int
_ltx(const void *p1, const void *p2)
{
return ((point *) p2)->dx - ((point *) p1)->dx;
}
#if 0
static int
_gty(const void *p1, const void *p2)
{
return ((point *) p1)->dy - ((point *) p2)->dy;
}
static int
_lty(const void *p1, const void *p2)
{
return ((point *) p2)->dy - ((point *) p1)->dy;
}
#endif
static void
sort_ints(int *is, int n, int ascending)
{
if (ascending >= 0)
qsort(is, n, sizeof(int), _gti);
else
qsort(is, n, sizeof(int), _lti);
}
static void
sort_points_x(point *ps, int n, int ascending)
{
if (ascending >= 0)
qsort(ps, n, sizeof(point), _gtx);
else
qsort(ps, n, sizeof(point), _ltx);
}
#if 0
static void
sort_points_y(point *ps, int n, int ascending)
{
if (ascending >= 0)
qsort(ps, n, sizeof(point), _gty);
else
qsort(ps, n, sizeof(point), _lty);
}
#endif
static void
uniq_ints(int *is, int *n)
{
int i, j;
for (i = 0, j = 0; i < *n - 1; i++)
{
if (is[i] != is[i + 1])
{
is[j] = is[i];
j++;
}
}
is[j] = is[i];
j++;
*n = j;
}
static void
filter_points(point *p, int *np, int max_dx, int max_dy)
{
int i, j, n = *np;
/* this is step 6 from above */
for (i = 0; i < n; i++)
{
if (p[i].dx < 0)
p[i].dominated = 1;
if (p[i].dy < 0)
p[i].dominated = 1;
/* clip to screen */
if (p[i].dx > max_dx)
p[i].dx = max_dx;
if (p[i].dy > max_dy)
p[i].dy = max_dy;
}
/* this is step 7 from above */
for (i = 0; i < n; i++)
{
if (p[i].dominated)
continue;
for (j = 0; j < n; j++)
{
if (i == j)
continue; /* self dominance = no */
if ((p[i].dx <= p[j].dx) && (p[i].dy <= p[j].dy))
{
p[j].dominated = 1;
}
}
}
/* actual pareto frontier filtering */
for (i = 0, j = 0; i < n; i++)
{
if (!p[i].dominated)
{
p[j] = p[i];
j++;
}
}
*np = j;
}
#define _get_span_xy pareto_maximizer
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)
{
int x, y, w, h;
int cx, cy; /* center */
int i, j, k;
point *constraints_tr = NULL; /* top right */
point *constraints_tl = NULL; /* top left */
point *constraints_br = NULL; /* botom right */
point *constraints_bl = NULL; /* botom left */
int num_tr, num_tl, num_br, num_bl;
int *top_ds = NULL;
int *bottom_ds = NULL;
point *left_ds = NULL;
point *right_ds = NULL;
int *tc, *bc;
point *lc, *rc; /* temp cursors */
int num_t, num_b, num_l, num_r;
float aspect;
EWin **filtered_lst, **stacked_lst, *pe, *pe2;
char *done_stacking_flag;
char *stacked_above_flag;
int num_stacked, stacked_above;
int area, new_area;
int recenter, new_recenter;
int td, bd, ld, rd; /* displacement to maximized edges */
Dprintf("searching within %d-%d %d-%d\n",
*avail_x1, *avail_x2, *avail_y1, *avail_y2);
x = EoGetX(ewin);
y = EoGetY(ewin);
h = EoGetH(ewin);
w = EoGetW(ewin);
cx = x + w / 2;
cy = y + h / 2;
/* center must be within available region */
cx = (cx >= *avail_x2) ? *avail_x2 - 1 : cx;
cy = (cy >= *avail_y2) ? *avail_y2 - 1 : cy;
cx = (cx < *avail_x1) ? *avail_x1 : cx;
cy = (cy < *avail_y1) ? *avail_y1 : cy;
filtered_lst = EMALLOC(EWin *, num);
stacked_lst = EMALLOC(EWin *, num + 1);
stacked_above_flag = ECALLOC(char, num);
done_stacking_flag = ECALLOC(char, num);
if (!filtered_lst || !stacked_lst || !done_stacking_flag ||
!stacked_above_flag)
goto freedom;
stacked_lst[0] = ewin;
num_stacked = 1;
/* ignore windows already overlapping ours and any windows UNDER them */
/* start by detecting windows we overlap */
for (i = 0, stacked_above = 0; i < num; i++)
{
pe = lst[i];
if (pe == ewin)
{
stacked_above = 1;
}
if ((pe == ewin) || _ignore(pe, type))
{
done_stacking_flag[i] = 1;
Dprintf("ignoring #%d %s\n", i, EwinGetTitle(pe));
continue;
}
if (SPANS_COMMON(x + 1, w - 2, EoGetX(pe), EoGetW(pe)) &&
SPANS_COMMON(y + 1, h - 2, EoGetY(pe), EoGetH(pe)))
{
stacked_lst[num_stacked] = pe;
stacked_above_flag[num_stacked] = stacked_above;
num_stacked++;
done_stacking_flag[i] = 1;
Dprintf("overlap #%d %s\n", i, EwinGetTitle(pe));
}
else
{
Dprintf("do not overlap #%d %s\n", i, EwinGetTitle(pe));
}
}
/* extend the stacked list to windows that are UNDER other items in the stacked list */
for (i = 1; i < num_stacked; i++)
{
int sx, sy, sw, sh;
pe2 = stacked_lst[i];
sx = EoGetX(pe2);
sy = EoGetY(pe2);
sh = EoGetH(pe2);
sw = EoGetW(pe2);
/* find stacked_lst window pe2 in the general window list */
for (j = 0; j < num; j++)
{
pe = lst[j];
if (pe == pe2)
break;
}
if (stacked_above_flag[i])
{
Dprintf("metaoverlap testing from understacked %s\n",
EwinGetTitle(pe));
}
else
{
Dprintf("NOT metaoverlap testing from overstacked %s\n",
EwinGetTitle(pe));
continue;
}
for (; j < num; j++)
{
pe = lst[j];
if (done_stacking_flag[j])
continue;
if (pe->props.never_use_area)
{
Dprintf("not using area %s\n", EwinGetTitle(pe));
continue;
}
if (SPANS_COMMON(sx + 1, sw - 2, EoGetX(pe), EoGetW(pe)) &&
SPANS_COMMON(sy + 1, sh - 2, EoGetY(pe), EoGetH(pe)))
{
/* the list is already top down so if it overlaps it's also under */
/* we hope! */
stacked_lst[num_stacked] = pe;
stacked_above_flag[num_stacked] = 1;
num_stacked++;
done_stacking_flag[j] = 1;
Dprintf("metaoverlap #%d %s\n", j, EwinGetTitle(pe));
}
else
{
Dprintf("no metaoverlap #%d %s\n", j, EwinGetTitle(pe));
}
}
}
/* copy remaining windows to our working set */
for (i = 0, j = 0; i < num; i++)
{
pe = lst[i];
if (done_stacking_flag[i])
{
Dprintf("no stacked constraint from #%d %s\n", i, EwinGetTitle(pe));
continue;
}
if ((pe == ewin) || _ignore(pe, type) ||
/* ignore windws that do not overlap with current search area */
!(SPANS_COMMON(*avail_x1, *avail_x2 - *avail_x1,
EoGetX(pe), EoGetW(pe)) &&
SPANS_COMMON(*avail_y1, *avail_y2 - *avail_y1,
EoGetY(pe), EoGetH(pe))))
{
Dprintf("no constraint from %s\n", EwinGetTitle(pe));
continue;
}
Dprintf("constraint from %s\n", EwinGetTitle(pe));
filtered_lst[j] = pe;
j++;
}
/* allocate memory to hold constraints */
num = j + 1;
num_tl = num_bl = num_br = num_tr = num * 5;
constraints_tl = EMALLOC(point, num * 5);
constraints_tr = EMALLOC(point, num * 5);
constraints_bl = EMALLOC(point, num * 5);
constraints_br = ECALLOC(point, num * 5);
if (!constraints_tl || !constraints_tr ||
!constraints_bl || !constraints_br)
goto freedom;
for (i = 1; i < num; i++)
{
pe = filtered_lst[i - 1];
/* store window corners as candidate constraints */
constraints_br[5 * i + 0].dx = EoGetX(pe);
constraints_br[5 * i + 0].dy = EoGetY(pe);
constraints_br[5 * i + 1].dx = EoGetX(pe) + EoGetW(pe);
constraints_br[5 * i + 1].dy = EoGetY(pe);
constraints_br[5 * i + 2].dx = EoGetX(pe) + EoGetW(pe);
constraints_br[5 * i + 2].dy = EoGetY(pe) + EoGetH(pe);
constraints_br[5 * i + 3].dx = EoGetX(pe);
constraints_br[5 * i + 3].dy = EoGetY(pe) + EoGetH(pe);
/* if window occupies 2 quadrants add a constraint for the
* intersection of the x or y axis and the window */
if (SPANS_COMMON(EoGetX(pe), EoGetW(pe), cx, 1))
{
Dprintf("got horiz edge contraint\n");
constraints_br[5 * i + 4].dx = cx;
if (EoGetY(pe) > cy)
{
constraints_br[5 * i + 4].dy = EoGetY(pe);
}
else
{
constraints_br[5 * i + 4].dy = EoGetY(pe) + EoGetH(pe);
}
}
else if (SPANS_COMMON(EoGetY(pe), EoGetH(pe), cy, 1))
{
Dprintf("got ver edge contraint\n");
constraints_br[5 * i + 4].dy = cy;
if (EoGetX(pe) > cx)
{
constraints_br[5 * i + 4].dx = EoGetX(pe);
}
else
{
constraints_br[5 * i + 4].dx = EoGetX(pe) + EoGetW(pe);
}
}
else
{
constraints_br[5 * i + 4].dominated = 1;
Dprintf("got no edge constraint from win #%d %s\n", i - 1,
EwinGetTitle(pe));
}
}
/* add constraints to keep the window on-screen */
constraints_br[0].dx = cx;
constraints_br[0].dy = *avail_y1 - 1;
constraints_br[1].dx = cx;
constraints_br[1].dy = *avail_y2 + 1;
constraints_br[2].dx = *avail_x1 - 1;
constraints_br[2].dy = cy;
constraints_br[3].dx = *avail_x2 + 1;
constraints_br[3].dy = cy;
constraints_br[4].dominated = 1;
/* subtract out center to get distance to constraints */
for (i = 0; i < num_tr; i++)
{
constraints_br[i].dx -= cx;
constraints_br[i].dy -= cy;
}
/* make 4 copies: one for each corner to optimize */
memcpy(constraints_tl, constraints_br, sizeof(point) * num_tl);
memcpy(constraints_bl, constraints_br, sizeof(point) * num_tl);
memcpy(constraints_tr, constraints_br, sizeof(point) * num_tl);
for (i = 0; i < num_tr; i++)
{
/* correct displacements to be positive in the direction of expansion */
constraints_tl[i].dx *= -1;
constraints_tl[i].dy *= -1;
constraints_bl[i].dx *= -1;
constraints_tr[i].dy *= -1;
}
/* bust out that pareto frontier */
filter_points(constraints_tl, &num_tl, cx - *avail_x1, cy - *avail_y1);
filter_points(constraints_tr, &num_tr, *avail_x2 - cx, cy - *avail_y1);
filter_points(constraints_bl, &num_bl, cx - *avail_x1, *avail_y2 - cy);
filter_points(constraints_br, &num_br, *avail_x2 - cx, *avail_y2 - cy);
/* now need to convert corner constraints to candidate edge constraints */
num_t = num_tl + num_tr;
num_b = num_bl + num_br;
num_l = num_tl + num_bl;
num_r = num_tr + num_br;
tc = top_ds = EMALLOC(int, num_t);
bc = bottom_ds = EMALLOC(int, num_b);
lc = left_ds = EMALLOC(point, num_l);
rc = right_ds = EMALLOC(point, num_r);
if (!tc || !bc || !lc || !rc)
goto freedom;
/* convert constraint distances back to to constraint displacements
* using cursor pointers to accumulate constraints for each edge */
for (i = 0; i < num_tr; i++)
{
rc->dx = constraints_tr[i].dx;
rc->dy = -constraints_tr[i].dy;
*tc = -constraints_tr[i].dy;
tc++;
rc++;
}
for (i = 0; i < num_tl; i++)
{
lc->dx = -constraints_tl[i].dx;
lc->dy = -constraints_tl[i].dy;
*tc = -constraints_tl[i].dy;
tc++;
lc++;
}
for (i = 0; i < num_br; i++)
{
rc->dx = constraints_br[i].dx;
rc->dy = constraints_br[i].dy;
*bc = constraints_br[i].dy;
bc++;
rc++;
}
for (i = 0; i < num_bl; i++)
{
lc->dx = -constraints_bl[i].dx;
lc->dy = constraints_bl[i].dy;
*bc = constraints_bl[i].dy;
bc++;
lc++;
}
/* sort the lists for easy searching */
sort_points_x(left_ds, num_l, +1);
sort_points_x(right_ds, num_r, -1);
sort_ints(top_ds, num_t, +1);
sort_ints(bottom_ds, num_b, -1);
uniq_ints(top_ds, &num_t);
uniq_ints(bottom_ds, &num_b);
/* brute force test the combinitorics:
* for each pair of possible top and bottom displacements,
* find the best possible pair of left and right displacements */
area = 0;
recenter = 0;
for (i = 0; i < num_t; i++) /* top indices */
{
for (j = 0; j < num_b; j++) /* bottom indices */
{
int new_w, new_h, trim;
td = top_ds[i]; /* displacement to top */
bd = bottom_ds[j]; /* displacement to bottom */
Dprintf("starting search in td-bd %d-%d\n", td, bd);
ld = left_ds[0].dx;
rd = right_ds[0].dx;
Dprintf("search in y from %d to %d\n", td, bd);
/* find furthest left given top, bottom */
for (k = 0; k < num_l; k++)
{
if (left_ds[k].dy <= td) /* constraint point is above top, skip it */
{
Dprintf("left ignoring above point %d,%d\n",
left_ds[k].dx, left_ds[k].dy);
continue;
}
if (left_ds[k].dy >= bd) /* constraint point is below bottom, skip it */
{
Dprintf("left ignoring below point %d,%d\n",
left_ds[k].dx, left_ds[k].dy);
continue;
}
Dprintf("left using point %d,%d\n", left_ds[k].dx,
left_ds[k].dy);
if (left_ds[k].dx > ld)
ld = left_ds[k].dx + 1;
}
/* find furthest right given top, bottom */
for (k = 0; k < num_r; k++)
{
if (right_ds[k].dy <= td) /* constraint point is above top, skip it */
{
Dprintf("right ignoring above point %d,%d\n",
right_ds[k].dx, right_ds[k].dy);
continue;
}
if (right_ds[k].dy >= bd) /* constraint point is below bottom, skip it */
{
Dprintf("right ignoring below point %d,%d\n",
right_ds[k].dx, right_ds[k].dy);
continue;
}
Dprintf("right using point %d,%d\n", right_ds[k].dx,
right_ds[k].dy);
if (right_ds[k].dx < rd)
rd = right_ds[k].dx - 1;
}
/* almost there..... need to correct for aspect ratio
* and keep center as close as possible to old center
* in case of duplicates...... */
new_w = 1 - ld + rd;
new_h = 1 - td + bd;
aspect = (float)new_w / new_h;
new_area = new_w * new_h;
if (new_area > area)
{
if (aspect > ewin->icccm.aspect_max)
{
do
{
if (abs(ld) < abs(rd))
{
Dprintf("trimming right for aspect\n");
rd--;
}
else
{
Dprintf("trimming left for aspect\n");
ld++;
}
new_w = 1 - ld + rd;
trim = new_w - (int)(new_h * ewin->icccm.aspect_min);
}
while (trim > 0);
Dprintf("ld now %d, rd now %d\n", ld, rd);
Dprintf("td now %d, bd now %d\n", td, bd);
Dprintf("\n");
}
else if (aspect < ewin->icccm.aspect_min)
{
do
{
if (abs(td) < abs(bd))
{
Dprintf("trimming bottom for aspect\n");
bd--;
}
else
{
Dprintf("trimming top for aspect\n");
td++;
}
new_h = 1 - td + bd;
trim = new_h - (int)(new_w / ewin->icccm.aspect_min);
}
while (trim > 0);
Dprintf("ld now %d, rd now %d\n", ld, rd);
Dprintf("td now %d, bd now %d\n", td, bd);
Dprintf("\n");
}
new_area = new_w * new_h;
new_recenter = abs(td) + abs(bd) + abs(ld) + abs(rd);
if ((new_area > area) ||
((new_area == area) && (new_recenter < recenter)))
{
Dprintf("new area %ld old area %ld\n", new_area, area);
Dprintf("%d-%d x %d-%d\n", x, x + w, y, y + h);
area = new_area;
recenter = new_recenter;
x = cx + ld;
y = cy + td;
w = (1 - ld + rd);
h = (1 - td + bd);
}
}
Dprintf("===========================\n");
}
}
*avail_x1 = x;
*avail_x2 = x + w;
*avail_y1 = y;
*avail_y2 = y + h;
/* rest a while */
freedom:
Efree(top_ds);
Efree(bottom_ds);
Efree(left_ds);
Efree(right_ds);
Efree(constraints_tl);
Efree(constraints_tr);
Efree(constraints_bl);
Efree(constraints_br);
Efree(filtered_lst);
Efree(stacked_lst);
Efree(done_stacking_flag);
Efree(stacked_above_flag);
}
#endif /* ENABLE_SMART_MAXIMISE */
void
MaxSizeHV(EWin *ewin, const char *resize_type, int hor, int ver, int flags)
{
int x, y, w, h, x1, x2, y1, y2, type, bl, br, bt, bb;
Area area;
EWin *const *lst;
int num, speed;
int old_hor = ewin->state.maximized_horz != 0;
int old_ver = ewin->state.maximized_vert != 0;
if (!ewin)
return;
type = MAX_ABSOLUTE; /* Select default */
if (!resize_type || !resize_type[0])
type = Conf.movres.mode_maximize_default;
else if (!strcmp(resize_type, "absolute"))
type = MAX_ABSOLUTE;
else if (!strcmp(resize_type, "available"))
type = MAX_AVAILABLE;
else if (!strcmp(resize_type, "conservative"))
type = MAX_CONSERVATIVE;
else if (!strcmp(resize_type, "xinerama"))
type = MAX_XINERAMA;
else if (!strcmp(resize_type, "half_N"))
type = MAX_HALF_N;
else if (!strcmp(resize_type, "half_S"))
type = MAX_HALF_S;
else if (!strcmp(resize_type, "half_E"))
type = MAX_HALF_E;
else if (!strcmp(resize_type, "half_W"))
type = MAX_HALF_W;
if (ewin->state.inhibit_max_hor)
hor = 0;
if (ewin->state.inhibit_max_ver)
ver = 0;
if (!hor && !ver)
return;
if ((type == MAX_HALF_E || type == MAX_HALF_W) && hor && !ver)
old_ver = 0;
if ((type == MAX_HALF_N || type == MAX_HALF_S) && ver && !hor)
old_hor = 0;
if (!old_hor && !old_ver)
{
ewin->save_max.x = EoGetX(ewin);
ewin->save_max.y = EoGetY(ewin);
ewin->save_max.w = ewin->client.w;
ewin->save_max.h = ewin->client.h;
}
/* Figure out target state */
if (hor && ver)
{
hor = ver = ((old_hor && old_ver) ? 0 : 1);
}
else
{
hor = (hor) ? !old_hor : old_hor;
ver = (ver) ? !old_ver : old_ver;
}
ewin->state.maximized_horz = hor;
ewin->state.maximized_vert = ver;
Dprintf("h/v old = %d/%d new=%d/%d\n", old_hor, old_ver, hor, ver);
if (!hor && !ver)
{
/* Restore regular state */
x = ewin->save_max.x;
y = ewin->save_max.y;
w = ewin->save_max.w;
h = ewin->save_max.h;
goto do_resize;
}
if (old_ver == ver && old_hor && !hor)
{
/* Turn off horizontal maxsize */
x = ewin->save_max.x;
y = EoGetY(ewin);
w = ewin->save_max.w;
h = ewin->client.h;
goto do_resize;
}
if (old_hor == hor && old_ver && !ver)
{
/* Turn off vertical maxsize */
x = EoGetX(ewin);
y = ewin->save_max.y;
w = ewin->client.w;
h = ewin->save_max.h;
goto do_resize;
}
/* Default is no change */
x = EoGetX(ewin);
y = EoGetY(ewin);
h = EoGetH(ewin);
w = EoGetW(ewin);
switch (type)
{
case MAX_XINERAMA:
if (hor)
{
x = 0;
w = WinGetW(VROOT);
}
if (ver)
{
y = 0;
h = WinGetH(VROOT);
}
break;
default:
case MAX_ABSOLUTE:
case MAX_AVAILABLE:
case MAX_CONSERVATIVE:
case MAX_HALF_N:
case MAX_HALF_S:
case MAX_HALF_E:
case MAX_HALF_W:
ScreenGetAvailableArea(x + w / 2, y + h / 2, &area,
Conf.place.ignore_struts_maximize);
x1 = area.x;
y1 = area.y;
x2 = x1 + area.w;
y2 = y1 + area.h;
if (Conf.movres.dragbar_nocover && type != MAX_ABSOLUTE)
{
/* Leave room for the dragbar */
switch (Conf.desks.dragdir)
{
case 0: /* left */
if (x1 < Conf.desks.dragbar_width)
x1 = Conf.desks.dragbar_width;
break;
case 1: /* right */
if (x2 > WinGetW(VROOT) - Conf.desks.dragbar_width)
x2 = WinGetW(VROOT) - Conf.desks.dragbar_width;
break;
case 2: /* top */
if (y1 < Conf.desks.dragbar_width)
y1 = Conf.desks.dragbar_width;
break;
case 3: /* bottom */
if (y2 > WinGetH(VROOT) - Conf.desks.dragbar_width)
y2 = WinGetH(VROOT) - Conf.desks.dragbar_width;
break;
default:
break;
}
}
if (type == MAX_ABSOLUTE)
{
/* Simply ignore all windows */
lst = NULL;
num = 0;
}
else
{
lst = EwinListGetForDesk(&num, EoGetDesk(ewin));
}
if (type == MAX_HALF_E && hor)
{
x1 += (x2 - x1) / 2;
}
if (type == MAX_HALF_W && hor)
{
x2 -= (x2 - x1) / 2;
}
if (type == MAX_HALF_N && ver)
{
y2 -= (y2 - y1) / 2;
}
if (type == MAX_HALF_S && ver)
{
y1 += (y2 - y1) / 2;
}
#if ENABLE_SMART_MAXIMISE
if (type == MAX_CONSERVATIVE && ver && hor &&
( /*(!old_hor && !old_ver) || */ Conf.movres.enable_smart_max_hv))
{
_get_span_xy(ewin, type, lst, num, &x1, &x2, &y1, &y2);
x = x1;
w = x2 - x1;
y = y1;
h = y2 - y1;
break;
}
#endif
if (ver)
{
_get_span_y(ewin, type, lst, num, &y1, &y2);
y = y1;
h = y2 - y1;
}
if (hor)
{
_get_span_x(ewin, type, lst, num, &x1, &x2);
x = x1;
w = x2 - x1;
}
break;
}
EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
w -= (bl + br);
if (w < 10)
w = 10;
h -= (bt + bb);
if (h < 10)
h = 10;
do_resize:
speed = Conf.movres.maximize_animate ? Conf.movres.maximize_speed : 0;
EwinSlideSizeTo(ewin, x, y, w, h, speed, 0, flags | SLIDE_WARP);
HintsSetWindowState(ewin);
}