xref: /openbsd-src/usr.bin/tmux/resize.c (revision b2140406a212bf0a749b68b28dcc118a55873e85)
1 /* $OpenBSD: resize.c,v 1.30 2019/03/16 17:14: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)
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);
54 
55 	/* Restore the window zoom state. */
56 	if (zoomed)
57 		window_zoom(w->active);
58 
59 	tty_update_window_offset(w);
60 	server_redraw_window(w);
61 	notify_window("window-layout-changed", w);
62 }
63 
64 static int
65 ignore_client_size(struct client *c)
66 {
67 	if (c->session == NULL)
68 		return (1);
69 	if (c->flags & CLIENT_NOSIZEFLAGS)
70 		return (1);
71 	if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
72 		return (1);
73 	return (0);
74 }
75 
76 void
77 default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy,
78     int type)
79 {
80 	struct client	*c;
81 	u_int		 cx, cy;
82 	const char	*value;
83 
84 	if (type == -1)
85 		type = options_get_number(global_w_options, "window-size");
86 	if (type == WINDOW_SIZE_MANUAL)
87 		goto manual;
88 
89 	if (type == WINDOW_SIZE_LARGEST) {
90 		*sx = *sy = 0;
91 		TAILQ_FOREACH(c, &clients, entry) {
92 			if (ignore_client_size(c))
93 				continue;
94 			if (w != NULL && !session_has(c->session, w))
95 				continue;
96 			if (w == NULL && c->session != s)
97 				continue;
98 
99 			cx = c->tty.sx;
100 			cy = c->tty.sy - status_line_size(c);
101 
102 			if (cx > *sx)
103 				*sx = cx;
104 			if (cy > *sy)
105 				*sy = cy;
106 		}
107 		if (*sx == 0 || *sy == 0)
108 			goto manual;
109 	} else {
110 		*sx = *sy = UINT_MAX;
111 		TAILQ_FOREACH(c, &clients, entry) {
112 			if (ignore_client_size(c))
113 				continue;
114 			if (w != NULL && !session_has(c->session, w))
115 				continue;
116 			if (w == NULL && c->session != s)
117 				continue;
118 
119 			cx = c->tty.sx;
120 			cy = c->tty.sy - status_line_size(c);
121 
122 			if (cx < *sx)
123 				*sx = cx;
124 			if (cy < *sy)
125 				*sy = cy;
126 		}
127 		if (*sx == UINT_MAX || *sy == UINT_MAX)
128 			goto manual;
129 	}
130 	goto done;
131 
132 manual:
133 	value = options_get_string(s->options, "default-size");
134 	if (sscanf(value, "%ux%u", sx, sy) != 2) {
135 		*sx = 80;
136 		*sy = 24;
137 	}
138 
139 done:
140 	if (*sx < WINDOW_MINIMUM)
141 		*sx = WINDOW_MINIMUM;
142 	if (*sx > WINDOW_MAXIMUM)
143 		*sx = WINDOW_MAXIMUM;
144 	if (*sy < WINDOW_MINIMUM)
145 		*sy = WINDOW_MINIMUM;
146 	if (*sy > WINDOW_MAXIMUM)
147 		*sy = WINDOW_MAXIMUM;
148 }
149 
150 void
151 recalculate_sizes(void)
152 {
153 	struct session	*s;
154 	struct client	*c;
155 	struct window	*w;
156 	u_int		 sx, sy, cx, cy;
157 	int		 type, current, has, changed;
158 
159 	/*
160 	 * Clear attached count and update saved status line information for
161 	 * each session.
162 	 */
163 	RB_FOREACH(s, sessions, &sessions) {
164 		s->attached = 0;
165 		status_update_cache(s);
166 	}
167 
168 	/*
169 	 * Increment attached count and check the status line size for each
170 	 * client.
171 	 */
172 	TAILQ_FOREACH(c, &clients, entry) {
173 		if (ignore_client_size(c))
174 			continue;
175 		if (c->tty.sy <= status_line_size(c))
176 			c->flags |= CLIENT_STATUSOFF;
177 		else
178 			c->flags &= ~CLIENT_STATUSOFF;
179 		c->session->attached++;
180 	}
181 
182 	/* Walk each window and adjust the size. */
183 	RB_FOREACH(w, windows, &windows) {
184 		if (w->active == NULL)
185 			continue;
186 		log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
187 
188 		type = options_get_number(w->options, "window-size");
189 		if (type == WINDOW_SIZE_MANUAL)
190 			continue;
191 		current = options_get_number(w->options, "aggressive-resize");
192 
193 		changed = 1;
194 		if (type == WINDOW_SIZE_LARGEST) {
195 			sx = sy = 0;
196 			TAILQ_FOREACH(c, &clients, entry) {
197 				if (ignore_client_size(c))
198 					continue;
199 				s = c->session;
200 
201 				if (current)
202 					has = (s->curw->window == w);
203 				else
204 					has = session_has(s, w);
205 				if (!has)
206 					continue;
207 
208 				cx = c->tty.sx;
209 				cy = c->tty.sy - status_line_size(c);
210 
211 				if (cx > sx)
212 					sx = cx;
213 				if (cy > sy)
214 					sy = cy;
215 			}
216 			if (sx == 0 || sy == 0)
217 				changed = 0;
218 		} else {
219 			sx = sy = UINT_MAX;
220 			TAILQ_FOREACH(c, &clients, entry) {
221 				if (ignore_client_size(c))
222 					continue;
223 				s = c->session;
224 
225 				if (current)
226 					has = (s->curw->window == w);
227 				else
228 					has = session_has(s, w);
229 				if (!has)
230 					continue;
231 
232 				cx = c->tty.sx;
233 				cy = c->tty.sy - status_line_size(c);
234 
235 				if (cx < sx)
236 					sx = cx;
237 				if (cy < sy)
238 					sy = cy;
239 			}
240 			if (sx == UINT_MAX || sy == UINT_MAX)
241 				changed = 0;
242 		}
243 		if (w->sx == sx && w->sy == sy)
244 			changed = 0;
245 
246 		if (!changed) {
247 			tty_update_window_offset(w);
248 			continue;
249 		}
250 		log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy);
251 		resize_window(w, sx, sy);
252 	}
253 }
254