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