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