/* * 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 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); }