Annotation of src/usr.bin/tmux/resize.c, Revision 1.42
1.42 ! nicm 1: /* $OpenBSD: resize.c,v 1.41 2020/06/05 07:33:57 nicm Exp $ */
1.1 nicm 2:
3: /*
1.19 nicm 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <string.h>
22:
23: #include "tmux.h"
24:
1.27 nicm 25: void
1.35 nicm 26: resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
1.27 nicm 27: {
28: int zoomed;
29:
30: /* Check size limits. */
31: if (sx < WINDOW_MINIMUM)
32: sx = WINDOW_MINIMUM;
33: if (sx > WINDOW_MAXIMUM)
34: sx = WINDOW_MAXIMUM;
35: if (sy < WINDOW_MINIMUM)
36: sy = WINDOW_MINIMUM;
37: if (sy > WINDOW_MAXIMUM)
38: sy = WINDOW_MAXIMUM;
39:
40: /* If the window is zoomed, unzoom. */
41: zoomed = w->flags & WINDOW_ZOOMED;
42: if (zoomed)
43: window_unzoom(w);
44:
45: /* Resize the layout first. */
46: layout_resize(w, sx, sy);
47:
48: /* Resize the window, it can be no smaller than the layout. */
49: if (sx < w->layout_root->sx)
50: sx = w->layout_root->sx;
51: if (sy < w->layout_root->sy)
52: sy = w->layout_root->sy;
1.35 nicm 53: window_resize(w, sx, sy, xpixel, ypixel);
1.31 nicm 54: log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id,
55: sx, sy, w->layout_root->sx, w->layout_root->sy);
1.27 nicm 56:
57: /* Restore the window zoom state. */
58: if (zoomed)
59: window_zoom(w->active);
60:
61: tty_update_window_offset(w);
62: server_redraw_window(w);
63: notify_window("window-layout-changed", w);
1.40 nicm 64: w->flags &= ~WINDOW_RESIZE;
1.27 nicm 65: }
1.1 nicm 66:
1.29 nicm 67: static int
68: ignore_client_size(struct client *c)
69: {
1.38 nicm 70: struct client *loop;
71:
1.29 nicm 72: if (c->session == NULL)
73: return (1);
74: if (c->flags & CLIENT_NOSIZEFLAGS)
75: return (1);
1.39 nicm 76: if (c->flags & CLIENT_IGNORESIZE) {
1.38 nicm 77: /*
1.39 nicm 78: * Ignore flagged clients if there are any attached clients
79: * that aren't flagged.
1.38 nicm 80: */
81: TAILQ_FOREACH (loop, &clients, entry) {
82: if (loop->session == NULL)
83: continue;
84: if (loop->flags & CLIENT_NOSIZEFLAGS)
85: continue;
1.39 nicm 86: if (~loop->flags & CLIENT_IGNORESIZE)
1.38 nicm 87: return (1);
88: }
89: }
1.29 nicm 90: if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
91: return (1);
92: return (0);
93: }
94:
1.42 ! nicm 95: static u_int
! 96: clients_with_window(struct window *w)
! 97: {
! 98: struct client *loop;
! 99: u_int n = 0;
! 100:
! 101: TAILQ_FOREACH(loop, &clients, entry) {
! 102: if (ignore_client_size(loop) || !session_has(loop->session, w))
! 103: continue;
! 104: if (++n > 1)
! 105: break;
! 106: }
! 107: return (n);
! 108: }
! 109:
! 110: static int
! 111: clients_calculate_size(int type, int current, struct session *s,
! 112: struct window *w, int (*skip_client)(struct client *, int, int,
! 113: struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel,
! 114: u_int *ypixel)
1.1 nicm 115: {
1.34 nicm 116: struct client *loop;
1.42 ! nicm 117: u_int cx, cy, n = 0;
1.27 nicm 118:
1.42 ! nicm 119: /* Manual windows do not have their size changed based on a client. */
! 120: if (type == WINDOW_SIZE_MANUAL)
! 121: return (0);
! 122:
! 123: /*
! 124: * Start comparing with 0 for largest and UINT_MAX for smallest or
! 125: * latest.
! 126: */
! 127: if (type == WINDOW_SIZE_LARGEST)
1.27 nicm 128: *sx = *sy = 0;
1.42 ! nicm 129: else
! 130: *sx = *sy = UINT_MAX;
! 131: *xpixel = *ypixel = 0;
! 132:
! 133: /*
! 134: * For latest, count the number of clients with this window. We only
! 135: * care if there is more than one.
! 136: */
! 137: if (type == WINDOW_SIZE_LATEST)
! 138: n = clients_with_window(w);
! 139:
! 140: /* Loop over the clients and work out the size. */
! 141: TAILQ_FOREACH(loop, &clients, entry) {
! 142: if (ignore_client_size(loop))
! 143: continue;
! 144: if (skip_client(loop, type, current, s, w))
! 145: continue;
! 146:
! 147: /*
! 148: * If there are multiple clients attached, only accept the
! 149: * latest client; otherwise let the only client be chosen as
! 150: * for smallest.
! 151: */
! 152: if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest)
! 153: continue;
1.27 nicm 154:
1.42 ! nicm 155: /* Work out this client's size. */
! 156: cx = loop->tty.sx;
! 157: cy = loop->tty.sy - status_line_size(loop);
1.9 nicm 158:
1.42 ! nicm 159: /*
! 160: * If it is larger or smaller than the best so far, update the
! 161: * new size.
! 162: */
! 163: if (type == WINDOW_SIZE_LARGEST) {
1.27 nicm 164: if (cx > *sx)
165: *sx = cx;
166: if (cy > *sy)
167: *sy = cy;
1.42 ! nicm 168: } else {
1.27 nicm 169: if (cx < *sx)
170: *sx = cx;
171: if (cy < *sy)
172: *sy = cy;
1.42 ! nicm 173: }
! 174: if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
! 175: *xpixel = loop->tty.xpixel;
! 176: *ypixel = loop->tty.ypixel;
! 177: }
! 178: }
1.35 nicm 179:
1.42 ! nicm 180: /* Return whether a suitable size was found. */
! 181: if (type == WINDOW_SIZE_LARGEST)
! 182: return (*sx != 0 && *sy != 0);
! 183: return (*sx != UINT_MAX && *sy != UINT_MAX);
! 184: }
! 185:
! 186: static int
! 187: default_window_size_skip_client (struct client *loop, int type,
! 188: __unused int current, struct session *s, struct window *w)
! 189: {
! 190: /*
! 191: * Latest checks separately, so do not check here. Otherwise only
! 192: * include clients where the session contains the window or where the
! 193: * session is the given session.
! 194: */
! 195: if (type == WINDOW_SIZE_LATEST)
! 196: return (0);
! 197: if (w != NULL && !session_has(loop->session, w))
! 198: return (1);
! 199: if (w == NULL && loop->session != s)
! 200: return (1);
! 201: return (0);
! 202: }
! 203:
! 204: void
! 205: default_window_size(struct client *c, struct session *s, struct window *w,
! 206: u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
! 207: {
! 208: const char *value;
! 209:
! 210: /* Get type if not provided. */
! 211: if (type == -1)
! 212: type = options_get_number(global_w_options, "window-size");
! 213:
! 214: /*
! 215: * Latest clients can use the given client if suitable. If there is no
! 216: * client and no window, use the default size as for manual type.
! 217: */
! 218: if (type == WINDOW_SIZE_LATEST) {
1.34 nicm 219: if (c != NULL && !ignore_client_size(c)) {
220: *sx = c->tty.sx;
221: *sy = c->tty.sy - status_line_size(c);
1.35 nicm 222: *xpixel = c->tty.xpixel;
1.42 ! nicm 223: *ypixel = c->tty.ypixel;
! 224: goto done;
1.34 nicm 225: }
1.42 ! nicm 226: if (w == NULL)
! 227: type = WINDOW_SIZE_MANUAL;
1.27 nicm 228: }
229:
1.42 ! nicm 230: /*
! 231: * Look for a client to base the size on. If none exists (or the type
! 232: * is manual), use the default-size option.
! 233: */
! 234: if (!clients_calculate_size(type, 0, s, w,
! 235: default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
! 236: value = options_get_string(s->options, "default-size");
! 237: if (sscanf(value, "%ux%u", sx, sy) != 2) {
! 238: *sx = 80;
! 239: *sy = 24;
! 240: }
1.27 nicm 241: }
242:
243: done:
1.42 ! nicm 244: /* Make sure the limits are enforced. */
1.27 nicm 245: if (*sx < WINDOW_MINIMUM)
246: *sx = WINDOW_MINIMUM;
247: if (*sx > WINDOW_MAXIMUM)
248: *sx = WINDOW_MAXIMUM;
249: if (*sy < WINDOW_MINIMUM)
250: *sy = WINDOW_MINIMUM;
251: if (*sy > WINDOW_MAXIMUM)
252: *sy = WINDOW_MAXIMUM;
253: }
1.1 nicm 254:
1.42 ! nicm 255: static int
! 256: recalculate_size_skip_client(struct client *loop, __unused int type,
! 257: int current, __unused struct session *s, struct window *w)
! 258: {
! 259: /*
! 260: * If the current flag is set, then skip any client where this window
! 261: * is not the current window - this is used for aggressive-resize.
! 262: * Otherwise skip any session that doesn't contain the window.
! 263: */
! 264: if (current)
! 265: return (loop->session->curw->window != w);
! 266: return (session_has(loop->session, w) == 0);
! 267: }
! 268:
1.27 nicm 269: void
1.41 nicm 270: recalculate_size(struct window *w, int now)
1.33 nicm 271: {
1.42 ! nicm 272: u_int sx, sy, xpixel = 0, ypixel = 0;
! 273: int type, current, changed;
1.33 nicm 274:
1.42 ! nicm 275: /*
! 276: * Do not attempt to resize windows which have no pane, they must be on
! 277: * the way to destruction.
! 278: */
1.33 nicm 279: if (w->active == NULL)
280: return;
281: log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
282:
1.42 ! nicm 283: /*
! 284: * Type is manual, smallest, largest, latest. Current is the
! 285: * aggressive-resize option (do not resize based on clients where the
! 286: * window is not the current window).
! 287: */
1.33 nicm 288: type = options_get_number(w->options, "window-size");
289: current = options_get_number(w->options, "aggressive-resize");
290:
1.42 ! nicm 291: /* Look for a suitable client and get the new size. */
! 292: changed = clients_calculate_size(type, current, NULL, w,
! 293: recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
! 294:
! 295: /*
! 296: * Make sure the size has actually changed. If the window has already
! 297: * got a resize scheduled, then use the new size; otherwise the old.
! 298: */
1.40 nicm 299: if (w->flags & WINDOW_RESIZE) {
1.41 nicm 300: if (!now && changed && w->new_sx == sx && w->new_sy == sy)
1.40 nicm 301: changed = 0;
302: } else {
1.41 nicm 303: if (!now && changed && w->sx == sx && w->sy == sy)
1.40 nicm 304: changed = 0;
305: }
1.33 nicm 306:
1.42 ! nicm 307: /*
! 308: * If the size hasn't changed, update the window offset but not the
! 309: * size.
! 310: */
1.33 nicm 311: if (!changed) {
312: tty_update_window_offset(w);
313: return;
314: }
1.42 ! nicm 315:
! 316: /*
! 317: * If the now flag is set or if the window is sized manually, change
! 318: * the size immediately. Otherwise set the flag and it will be done
! 319: * later.
! 320: */
1.40 nicm 321: log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
1.41 nicm 322: if (now || type == WINDOW_SIZE_MANUAL)
1.40 nicm 323: resize_window(w, sx, sy, xpixel, ypixel);
324: else {
325: w->new_sx = sx;
326: w->new_sy = sy;
327: w->new_xpixel = xpixel;
328: w->new_ypixel = ypixel;
329:
330: w->flags |= WINDOW_RESIZE;
331: tty_update_window_offset(w);
332: }
1.33 nicm 333: }
334:
335: void
1.27 nicm 336: recalculate_sizes(void)
337: {
1.41 nicm 338: recalculate_sizes_now(0);
339: }
340:
341: void
342: recalculate_sizes_now(int now)
343: {
1.27 nicm 344: struct session *s;
345: struct client *c;
346: struct window *w;
347:
348: /*
349: * Clear attached count and update saved status line information for
350: * each session.
351: */
352: RB_FOREACH(s, sessions, &sessions) {
353: s->attached = 0;
1.30 nicm 354: status_update_cache(s);
1.27 nicm 355: }
1.9 nicm 356:
1.27 nicm 357: /*
358: * Increment attached count and check the status line size for each
359: * client.
360: */
361: TAILQ_FOREACH(c, &clients, entry) {
1.37 nicm 362: s = c->session;
363: if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
364: s->attached++;
1.29 nicm 365: if (ignore_client_size(c))
1.27 nicm 366: continue;
1.32 nicm 367: if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
1.27 nicm 368: c->flags |= CLIENT_STATUSOFF;
369: else
370: c->flags &= ~CLIENT_STATUSOFF;
1.1 nicm 371: }
372:
1.27 nicm 373: /* Walk each window and adjust the size. */
1.33 nicm 374: RB_FOREACH(w, windows, &windows)
1.41 nicm 375: recalculate_size(w, now);
1.1 nicm 376: }