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 | |
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 | 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 | |
94 | void |
95 | default_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 | |
210 | manual: |
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 | |
217 | done: |
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 | |
228 | void |
229 | recalculate_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 | |
361 | void |
362 | recalculate_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 | |