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