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