Annotation of src/usr.bin/tmux/resize.c, Revision 1.44
1.44 ! nicm 1: /* $OpenBSD: resize.c,v 1.43 2021/06/10 07:24:45 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
1.43 nicm 111: clients_calculate_size(int type, int current, struct client *c,
112: struct session *s, struct window *w, int (*skip_client)(struct client *,
113: int, int, struct session *, struct window *), u_int *sx, u_int *sy,
114: u_int *xpixel, 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. */
1.43 nicm 120: if (type == WINDOW_SIZE_MANUAL) {
121: log_debug("%s: type is manual", __func__);
1.42 nicm 122: return (0);
1.43 nicm 123: }
1.42 nicm 124:
125: /*
126: * Start comparing with 0 for largest and UINT_MAX for smallest or
127: * latest.
128: */
129: if (type == WINDOW_SIZE_LARGEST)
1.27 nicm 130: *sx = *sy = 0;
1.42 nicm 131: else
132: *sx = *sy = UINT_MAX;
133: *xpixel = *ypixel = 0;
134:
135: /*
136: * For latest, count the number of clients with this window. We only
137: * care if there is more than one.
138: */
1.44 ! nicm 139: if (type == WINDOW_SIZE_LATEST && w != NULL)
1.42 nicm 140: n = clients_with_window(w);
141:
142: /* Loop over the clients and work out the size. */
143: TAILQ_FOREACH(loop, &clients, entry) {
1.43 nicm 144: if (loop != c && ignore_client_size(loop)) {
145: log_debug("%s: ignoring %s", __func__, loop->name);
1.42 nicm 146: continue;
1.43 nicm 147: }
148: if (loop != c && skip_client(loop, type, current, s, w)) {
149: log_debug("%s: skipping %s", __func__, loop->name);
1.42 nicm 150: continue;
1.43 nicm 151: }
1.42 nicm 152:
153: /*
154: * If there are multiple clients attached, only accept the
155: * latest client; otherwise let the only client be chosen as
156: * for smallest.
157: */
1.43 nicm 158: if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) {
159: log_debug("%s: %s is not latest", __func__, loop->name);
1.42 nicm 160: continue;
1.43 nicm 161: }
1.27 nicm 162:
1.42 nicm 163: /* Work out this client's size. */
164: cx = loop->tty.sx;
165: cy = loop->tty.sy - status_line_size(loop);
1.9 nicm 166:
1.42 nicm 167: /*
168: * If it is larger or smaller than the best so far, update the
169: * new size.
170: */
171: if (type == WINDOW_SIZE_LARGEST) {
1.27 nicm 172: if (cx > *sx)
173: *sx = cx;
174: if (cy > *sy)
175: *sy = cy;
1.42 nicm 176: } else {
1.27 nicm 177: if (cx < *sx)
178: *sx = cx;
179: if (cy < *sy)
180: *sy = cy;
1.42 nicm 181: }
182: if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
183: *xpixel = loop->tty.xpixel;
184: *ypixel = loop->tty.ypixel;
185: }
1.43 nicm 186: log_debug("%s: after %s (%ux%u), size is %ux%u", __func__,
187: loop->name, cx, cy, *sx, *sy);
1.42 nicm 188: }
1.35 nicm 189:
1.42 nicm 190: /* Return whether a suitable size was found. */
1.43 nicm 191: if (type == WINDOW_SIZE_LARGEST) {
192: log_debug("%s: type is largest", __func__);
1.42 nicm 193: return (*sx != 0 && *sy != 0);
1.43 nicm 194: }
195: if (type == WINDOW_SIZE_LATEST)
196: log_debug("%s: type is latest", __func__);
197: else
198: log_debug("%s: type is smallest", __func__);
1.42 nicm 199: return (*sx != UINT_MAX && *sy != UINT_MAX);
200: }
201:
202: static int
1.43 nicm 203: default_window_size_skip_client(struct client *loop, int type,
1.42 nicm 204: __unused int current, struct session *s, struct window *w)
205: {
206: /*
207: * Latest checks separately, so do not check here. Otherwise only
208: * include clients where the session contains the window or where the
209: * session is the given session.
210: */
211: if (type == WINDOW_SIZE_LATEST)
212: return (0);
213: if (w != NULL && !session_has(loop->session, w))
214: return (1);
215: if (w == NULL && loop->session != s)
216: return (1);
217: return (0);
218: }
219:
220: void
221: default_window_size(struct client *c, struct session *s, struct window *w,
222: u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
223: {
224: const char *value;
225:
226: /* Get type if not provided. */
227: if (type == -1)
228: type = options_get_number(global_w_options, "window-size");
229:
230: /*
231: * Latest clients can use the given client if suitable. If there is no
232: * client and no window, use the default size as for manual type.
233: */
234: if (type == WINDOW_SIZE_LATEST) {
1.34 nicm 235: if (c != NULL && !ignore_client_size(c)) {
236: *sx = c->tty.sx;
237: *sy = c->tty.sy - status_line_size(c);
1.35 nicm 238: *xpixel = c->tty.xpixel;
1.42 nicm 239: *ypixel = c->tty.ypixel;
1.43 nicm 240: log_debug("%s: using %ux%u from %s", __func__, *sx, *sy,
241: c->name);
1.42 nicm 242: goto done;
1.34 nicm 243: }
1.27 nicm 244: }
245:
1.42 nicm 246: /*
247: * Look for a client to base the size on. If none exists (or the type
248: * is manual), use the default-size option.
249: */
1.43 nicm 250: if (!clients_calculate_size(type, 0, c, s, w,
1.42 nicm 251: default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
252: value = options_get_string(s->options, "default-size");
253: if (sscanf(value, "%ux%u", sx, sy) != 2) {
254: *sx = 80;
255: *sy = 24;
256: }
1.43 nicm 257: log_debug("%s: using %ux%u from default-size", __func__, *sx,
258: *sy);
1.27 nicm 259: }
260:
261: done:
1.42 nicm 262: /* Make sure the limits are enforced. */
1.27 nicm 263: if (*sx < WINDOW_MINIMUM)
264: *sx = WINDOW_MINIMUM;
265: if (*sx > WINDOW_MAXIMUM)
266: *sx = WINDOW_MAXIMUM;
267: if (*sy < WINDOW_MINIMUM)
268: *sy = WINDOW_MINIMUM;
269: if (*sy > WINDOW_MAXIMUM)
270: *sy = WINDOW_MAXIMUM;
1.43 nicm 271: log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy);
1.27 nicm 272: }
1.1 nicm 273:
1.42 nicm 274: static int
275: recalculate_size_skip_client(struct client *loop, __unused int type,
276: int current, __unused struct session *s, struct window *w)
277: {
278: /*
279: * If the current flag is set, then skip any client where this window
280: * is not the current window - this is used for aggressive-resize.
281: * Otherwise skip any session that doesn't contain the window.
282: */
283: if (current)
284: return (loop->session->curw->window != w);
285: return (session_has(loop->session, w) == 0);
286: }
287:
1.27 nicm 288: void
1.41 nicm 289: recalculate_size(struct window *w, int now)
1.33 nicm 290: {
1.42 nicm 291: u_int sx, sy, xpixel = 0, ypixel = 0;
292: int type, current, changed;
1.33 nicm 293:
1.42 nicm 294: /*
295: * Do not attempt to resize windows which have no pane, they must be on
296: * the way to destruction.
297: */
1.33 nicm 298: if (w->active == NULL)
299: return;
300: log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
301:
1.42 nicm 302: /*
303: * Type is manual, smallest, largest, latest. Current is the
304: * aggressive-resize option (do not resize based on clients where the
305: * window is not the current window).
306: */
1.33 nicm 307: type = options_get_number(w->options, "window-size");
308: current = options_get_number(w->options, "aggressive-resize");
309:
1.42 nicm 310: /* Look for a suitable client and get the new size. */
1.43 nicm 311: changed = clients_calculate_size(type, current, NULL, NULL, w,
1.42 nicm 312: recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
313:
314: /*
315: * Make sure the size has actually changed. If the window has already
316: * got a resize scheduled, then use the new size; otherwise the old.
317: */
1.40 nicm 318: if (w->flags & WINDOW_RESIZE) {
1.41 nicm 319: if (!now && changed && w->new_sx == sx && w->new_sy == sy)
1.40 nicm 320: changed = 0;
321: } else {
1.41 nicm 322: if (!now && changed && w->sx == sx && w->sy == sy)
1.40 nicm 323: changed = 0;
324: }
1.33 nicm 325:
1.42 nicm 326: /*
327: * If the size hasn't changed, update the window offset but not the
328: * size.
329: */
1.33 nicm 330: if (!changed) {
331: tty_update_window_offset(w);
332: return;
333: }
1.42 nicm 334:
335: /*
336: * If the now flag is set or if the window is sized manually, change
337: * the size immediately. Otherwise set the flag and it will be done
338: * later.
339: */
1.40 nicm 340: log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
1.41 nicm 341: if (now || type == WINDOW_SIZE_MANUAL)
1.40 nicm 342: resize_window(w, sx, sy, xpixel, ypixel);
343: else {
344: w->new_sx = sx;
345: w->new_sy = sy;
346: w->new_xpixel = xpixel;
347: w->new_ypixel = ypixel;
348:
349: w->flags |= WINDOW_RESIZE;
350: tty_update_window_offset(w);
351: }
1.33 nicm 352: }
353:
354: void
1.27 nicm 355: recalculate_sizes(void)
356: {
1.41 nicm 357: recalculate_sizes_now(0);
358: }
359:
360: void
361: recalculate_sizes_now(int now)
362: {
1.27 nicm 363: struct session *s;
364: struct client *c;
365: struct window *w;
366:
367: /*
368: * Clear attached count and update saved status line information for
369: * each session.
370: */
371: RB_FOREACH(s, sessions, &sessions) {
372: s->attached = 0;
1.30 nicm 373: status_update_cache(s);
1.27 nicm 374: }
1.9 nicm 375:
1.27 nicm 376: /*
377: * Increment attached count and check the status line size for each
378: * client.
379: */
380: TAILQ_FOREACH(c, &clients, entry) {
1.37 nicm 381: s = c->session;
382: if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
383: s->attached++;
1.29 nicm 384: if (ignore_client_size(c))
1.27 nicm 385: continue;
1.32 nicm 386: if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
1.27 nicm 387: c->flags |= CLIENT_STATUSOFF;
388: else
389: c->flags &= ~CLIENT_STATUSOFF;
1.1 nicm 390: }
391:
1.27 nicm 392: /* Walk each window and adjust the size. */
1.33 nicm 393: RB_FOREACH(w, windows, &windows)
1.41 nicm 394: recalculate_size(w, now);
1.1 nicm 395: }