xref: /openbsd-src/usr.bin/tmux/resize.c (revision 505ee9ea3b177e2387d907a91ca7da069f3f14d8)
1 /* $OpenBSD: resize.c,v 1.41 2020/06/05 07:33:57 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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 
25 void
26 resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
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;
53 	window_resize(w, sx, sy, xpixel, ypixel);
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);
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);
64 	w->flags &= ~WINDOW_RESIZE;
65 }
66 
67 static int
68 ignore_client_size(struct client *c)
69 {
70 	struct client	*loop;
71 
72 	if (c->session == NULL)
73 		return (1);
74 	if (c->flags & CLIENT_NOSIZEFLAGS)
75 		return (1);
76 	if (c->flags & CLIENT_IGNORESIZE) {
77 		/*
78 		 * Ignore flagged clients if there are any attached clients
79 		 * that aren't flagged.
80 		 */
81 		TAILQ_FOREACH (loop, &clients, entry) {
82 			if (loop->session == NULL)
83 				continue;
84 			if (loop->flags & CLIENT_NOSIZEFLAGS)
85 				continue;
86 			if (~loop->flags & CLIENT_IGNORESIZE)
87 				return (1);
88 		}
89 	}
90 	if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
91 		return (1);
92 	return (0);
93 }
94 
95 void
96 default_window_size(struct client *c, struct session *s, struct window *w,
97     u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
98 {
99 	struct client	*loop;
100 	u_int		 cx, cy, n;
101 	const char	*value;
102 
103 	if (type == -1)
104 		type = options_get_number(global_w_options, "window-size");
105 	switch (type) {
106 	case WINDOW_SIZE_LARGEST:
107 		*sx = *sy = 0;
108 		*xpixel = *ypixel = 0;
109 		TAILQ_FOREACH(loop, &clients, entry) {
110 			if (ignore_client_size(loop))
111 				continue;
112 			if (w != NULL && !session_has(loop->session, w))
113 				continue;
114 			if (w == NULL && loop->session != s)
115 				continue;
116 
117 			cx = loop->tty.sx;
118 			cy = loop->tty.sy - status_line_size(loop);
119 
120 			if (cx > *sx)
121 				*sx = cx;
122 			if (cy > *sy)
123 				*sy = cy;
124 
125 			if (loop->tty.xpixel > *xpixel &&
126 			    loop->tty.ypixel > *ypixel) {
127 				*xpixel = loop->tty.xpixel;
128 				*ypixel = loop->tty.ypixel;
129 			}
130 		}
131 		if (*sx == 0 || *sy == 0)
132 			goto manual;
133 		break;
134 	case WINDOW_SIZE_SMALLEST:
135 		*sx = *sy = UINT_MAX;
136 		*xpixel = *ypixel = 0;
137 		TAILQ_FOREACH(loop, &clients, entry) {
138 			if (ignore_client_size(loop))
139 				continue;
140 			if (w != NULL && !session_has(loop->session, w))
141 				continue;
142 			if (w == NULL && loop->session != s)
143 				continue;
144 
145 			cx = loop->tty.sx;
146 			cy = loop->tty.sy - status_line_size(loop);
147 
148 			if (cx < *sx)
149 				*sx = cx;
150 			if (cy < *sy)
151 				*sy = cy;
152 
153 			if (loop->tty.xpixel > *xpixel &&
154 			    loop->tty.ypixel > *ypixel) {
155 				*xpixel = loop->tty.xpixel;
156 				*ypixel = loop->tty.ypixel;
157 			}
158 		}
159 		if (*sx == UINT_MAX || *sy == UINT_MAX)
160 			goto manual;
161 		break;
162 	case WINDOW_SIZE_LATEST:
163 		if (c != NULL && !ignore_client_size(c)) {
164 			*sx = c->tty.sx;
165 			*sy = c->tty.sy - status_line_size(c);
166 			*xpixel = c->tty.xpixel;
167 		        *ypixel = c->tty.ypixel;
168 		} else {
169 			if (w == NULL)
170 				goto manual;
171 			n = 0;
172 			TAILQ_FOREACH(loop, &clients, entry) {
173 				if (!ignore_client_size(loop) &&
174 				    session_has(loop->session, w)) {
175 					if (++n > 1)
176 						break;
177 				}
178 			}
179 			*sx = *sy = UINT_MAX;
180 			*xpixel = *ypixel = 0;
181 			TAILQ_FOREACH(loop, &clients, entry) {
182 				if (ignore_client_size(loop))
183 					continue;
184 				if (n > 1 && loop != w->latest)
185 					continue;
186 				s = loop->session;
187 
188 				cx = loop->tty.sx;
189 				cy = loop->tty.sy - status_line_size(loop);
190 
191 				if (cx < *sx)
192 					*sx = cx;
193 				if (cy < *sy)
194 					*sy = cy;
195 
196 				if (loop->tty.xpixel > *xpixel &&
197 				    loop->tty.ypixel > *ypixel) {
198 					*xpixel = loop->tty.xpixel;
199 					*ypixel = loop->tty.ypixel;
200 				}
201 			}
202 			if (*sx == UINT_MAX || *sy == UINT_MAX)
203 				goto manual;
204 		}
205 		break;
206 	case WINDOW_SIZE_MANUAL:
207 		goto manual;
208 	}
209 	goto done;
210 
211 manual:
212 	value = options_get_string(s->options, "default-size");
213 	if (sscanf(value, "%ux%u", sx, sy) != 2) {
214 		*sx = 80;
215 		*sy = 24;
216 	}
217 
218 done:
219 	if (*sx < WINDOW_MINIMUM)
220 		*sx = WINDOW_MINIMUM;
221 	if (*sx > WINDOW_MAXIMUM)
222 		*sx = WINDOW_MAXIMUM;
223 	if (*sy < WINDOW_MINIMUM)
224 		*sy = WINDOW_MINIMUM;
225 	if (*sy > WINDOW_MAXIMUM)
226 		*sy = WINDOW_MAXIMUM;
227 }
228 
229 void
230 recalculate_size(struct window *w, int now)
231 {
232 	struct session	*s;
233 	struct client	*c;
234 	u_int		 sx, sy, cx, cy, xpixel = 0, ypixel = 0, n;
235 	int		 type, current, has, changed;
236 
237 	if (w->active == NULL)
238 		return;
239 	log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
240 
241 	type = options_get_number(w->options, "window-size");
242 	current = options_get_number(w->options, "aggressive-resize");
243 
244 	changed = 1;
245 	switch (type) {
246 	case WINDOW_SIZE_LARGEST:
247 		sx = sy = 0;
248 		TAILQ_FOREACH(c, &clients, entry) {
249 			if (ignore_client_size(c))
250 				continue;
251 			s = c->session;
252 
253 			if (current)
254 				has = (s->curw->window == w);
255 			else
256 				has = session_has(s, w);
257 			if (!has)
258 				continue;
259 
260 			cx = c->tty.sx;
261 			cy = c->tty.sy - status_line_size(c);
262 
263 			if (cx > sx)
264 				sx = cx;
265 			if (cy > sy)
266 				sy = cy;
267 
268 			if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
269 				xpixel = c->tty.xpixel;
270 				ypixel = c->tty.ypixel;
271 			}
272 		}
273 		if (sx == 0 || sy == 0)
274 			changed = 0;
275 		break;
276 	case WINDOW_SIZE_SMALLEST:
277 		sx = sy = UINT_MAX;
278 		TAILQ_FOREACH(c, &clients, entry) {
279 			if (ignore_client_size(c))
280 				continue;
281 			s = c->session;
282 
283 			if (current)
284 				has = (s->curw->window == w);
285 			else
286 				has = session_has(s, w);
287 			if (!has)
288 				continue;
289 
290 			cx = c->tty.sx;
291 			cy = c->tty.sy - status_line_size(c);
292 
293 			if (cx < sx)
294 				sx = cx;
295 			if (cy < sy)
296 				sy = cy;
297 
298 			if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
299 				xpixel = c->tty.xpixel;
300 				ypixel = c->tty.ypixel;
301 			}
302 		}
303 		if (sx == UINT_MAX || sy == UINT_MAX)
304 			changed = 0;
305 		break;
306 	case WINDOW_SIZE_LATEST:
307 		n = 0;
308 		TAILQ_FOREACH(c, &clients, entry) {
309 			if (!ignore_client_size(c) &&
310 			    session_has(c->session, w)) {
311 				if (++n > 1)
312 					break;
313 			}
314 		}
315 		sx = sy = UINT_MAX;
316 		TAILQ_FOREACH(c, &clients, entry) {
317 			if (ignore_client_size(c))
318 				continue;
319 			if (n > 1 && c != w->latest)
320 				continue;
321 			s = c->session;
322 
323 			if (current)
324 				has = (s->curw->window == w);
325 			else
326 				has = session_has(s, w);
327 			if (!has)
328 				continue;
329 
330 			cx = c->tty.sx;
331 			cy = c->tty.sy - status_line_size(c);
332 
333 			if (cx < sx)
334 				sx = cx;
335 			if (cy < sy)
336 				sy = cy;
337 
338 			if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
339 				xpixel = c->tty.xpixel;
340 				ypixel = c->tty.ypixel;
341 			}
342 		}
343 		if (sx == UINT_MAX || sy == UINT_MAX)
344 			changed = 0;
345 		break;
346 	case WINDOW_SIZE_MANUAL:
347 		changed = 0;
348 		break;
349 	}
350 	if (w->flags & WINDOW_RESIZE) {
351 		if (!now && changed && w->new_sx == sx && w->new_sy == sy)
352 			changed = 0;
353 	} else {
354 		if (!now && changed && w->sx == sx && w->sy == sy)
355 			changed = 0;
356 	}
357 
358 	if (!changed) {
359 		tty_update_window_offset(w);
360 		return;
361 	}
362 	log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
363 	if (now || type == WINDOW_SIZE_MANUAL)
364 		resize_window(w, sx, sy, xpixel, ypixel);
365 	else {
366 		w->new_sx = sx;
367 		w->new_sy = sy;
368 		w->new_xpixel = xpixel;
369 		w->new_ypixel = ypixel;
370 
371 		w->flags |= WINDOW_RESIZE;
372 		tty_update_window_offset(w);
373 	}
374 }
375 
376 void
377 recalculate_sizes(void)
378 {
379 	recalculate_sizes_now(0);
380 }
381 
382 void
383 recalculate_sizes_now(int now)
384 {
385 	struct session	*s;
386 	struct client	*c;
387 	struct window	*w;
388 
389 	/*
390 	 * Clear attached count and update saved status line information for
391 	 * each session.
392 	 */
393 	RB_FOREACH(s, sessions, &sessions) {
394 		s->attached = 0;
395 		status_update_cache(s);
396 	}
397 
398 	/*
399 	 * Increment attached count and check the status line size for each
400 	 * client.
401 	 */
402 	TAILQ_FOREACH(c, &clients, entry) {
403 		s = c->session;
404 		if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
405 			s->attached++;
406 		if (ignore_client_size(c))
407 			continue;
408 		if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
409 			c->flags |= CLIENT_STATUSOFF;
410 		else
411 			c->flags &= ~CLIENT_STATUSOFF;
412 	}
413 
414 	/* Walk each window and adjust the size. */
415 	RB_FOREACH(w, windows, &windows)
416 		recalculate_size(w, now);
417 }
418