File: | src/usr.bin/tmux/resize.c |
Warning: | line 457, column 20 Access to field 'statuslines' results in a dereference of a null pointer (loaded from variable 's') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: resize.c,v 1.49 2022/01/06 08:20:00 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, int xpixel, int ypixel) | |||
27 | { | |||
28 | int zoomed; | |||
29 | ||||
30 | /* Check size limits. */ | |||
31 | if (sx < WINDOW_MINIMUM1) | |||
32 | sx = WINDOW_MINIMUM1; | |||
33 | if (sx > WINDOW_MAXIMUM10000) | |||
34 | sx = WINDOW_MAXIMUM10000; | |||
35 | if (sy < WINDOW_MINIMUM1) | |||
36 | sy = WINDOW_MINIMUM1; | |||
37 | if (sy > WINDOW_MAXIMUM10000) | |||
38 | sy = WINDOW_MAXIMUM10000; | |||
39 | ||||
40 | /* If the window is zoomed, unzoom. */ | |||
41 | zoomed = w->flags & WINDOW_ZOOMED0x8; | |||
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 %ux%u; layout %ux%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 | w->flags &= ~WINDOW_RESIZE0x20; | |||
65 | } | |||
66 | ||||
67 | static int | |||
68 | ignore_client_size(struct client *c) | |||
69 | { | |||
70 | struct client *loop; | |||
71 | ||||
72 | if (c->session == NULL((void *)0)) | |||
73 | return (1); | |||
74 | if (c->flags & CLIENT_NOSIZEFLAGS(0x200| 0x40| 0x4)) | |||
75 | return (1); | |||
76 | if (c->flags & CLIENT_IGNORESIZE0x20000) { | |||
77 | /* | |||
78 | * Ignore flagged clients if there are any attached clients | |||
79 | * that aren't flagged. | |||
80 | */ | |||
81 | TAILQ_FOREACH (loop, &clients, entry)for((loop) = ((&clients)->tqh_first); (loop) != ((void *)0); (loop) = ((loop)->entry.tqe_next)) { | |||
82 | if (loop->session == NULL((void *)0)) | |||
83 | continue; | |||
84 | if (loop->flags & CLIENT_NOSIZEFLAGS(0x200| 0x40| 0x4)) | |||
85 | continue; | |||
86 | if (~loop->flags & CLIENT_IGNORESIZE0x20000) | |||
87 | return (1); | |||
88 | } | |||
89 | } | |||
90 | if ((c->flags & CLIENT_CONTROL0x2000) && | |||
91 | (~c->flags & CLIENT_SIZECHANGED0x400000) && | |||
92 | (~c->flags & CLIENT_WINDOWSIZECHANGED0x400000000ULL)) | |||
93 | return (1); | |||
94 | return (0); | |||
95 | } | |||
96 | ||||
97 | static u_int | |||
98 | clients_with_window(struct window *w) | |||
99 | { | |||
100 | struct client *loop; | |||
101 | u_int n = 0; | |||
102 | ||||
103 | TAILQ_FOREACH(loop, &clients, entry)for((loop) = ((&clients)->tqh_first); (loop) != ((void *)0); (loop) = ((loop)->entry.tqe_next)) { | |||
104 | if (ignore_client_size(loop) || !session_has(loop->session, w)) | |||
105 | continue; | |||
106 | if (++n > 1) | |||
107 | break; | |||
108 | } | |||
109 | return (n); | |||
110 | } | |||
111 | ||||
112 | static int | |||
113 | clients_calculate_size(int type, int current, struct client *c, | |||
114 | struct session *s, struct window *w, int (*skip_client)(struct client *, | |||
115 | int, int, struct session *, struct window *), u_int *sx, u_int *sy, | |||
116 | u_int *xpixel, u_int *ypixel) | |||
117 | { | |||
118 | struct client *loop; | |||
119 | struct client_window *cw; | |||
120 | u_int cx, cy, n = 0; | |||
121 | ||||
122 | /* | |||
123 | * Start comparing with 0 for largest and UINT_MAX for smallest or | |||
124 | * latest. | |||
125 | */ | |||
126 | if (type == WINDOW_SIZE_LARGEST0) { | |||
127 | *sx = 0; | |||
128 | *sy = 0; | |||
129 | } else if (type == WINDOW_SIZE_MANUAL2) { | |||
130 | *sx = w->manual_sx; | |||
131 | *sy = w->manual_sy; | |||
132 | log_debug("%s: manual size %ux%u", __func__, *sx, *sy); | |||
133 | } else { | |||
134 | *sx = UINT_MAX(2147483647 *2U +1U); | |||
135 | *sy = UINT_MAX(2147483647 *2U +1U); | |||
136 | } | |||
137 | *xpixel = *ypixel = 0; | |||
138 | ||||
139 | /* | |||
140 | * For latest, count the number of clients with this window. We only | |||
141 | * care if there is more than one. | |||
142 | */ | |||
143 | if (type == WINDOW_SIZE_LATEST3 && w != NULL((void *)0)) | |||
144 | n = clients_with_window(w); | |||
145 | ||||
146 | /* Skip setting the size if manual */ | |||
147 | if (type == WINDOW_SIZE_MANUAL2) | |||
148 | goto skip; | |||
149 | ||||
150 | /* Loop over the clients and work out the size. */ | |||
151 | TAILQ_FOREACH(loop, &clients, entry)for((loop) = ((&clients)->tqh_first); (loop) != ((void *)0); (loop) = ((loop)->entry.tqe_next)) { | |||
152 | if (loop != c && ignore_client_size(loop)) { | |||
153 | log_debug("%s: ignoring %s (1)", __func__, loop->name); | |||
154 | continue; | |||
155 | } | |||
156 | if (loop != c && skip_client(loop, type, current, s, w)) { | |||
157 | log_debug("%s: skipping %s (1)", __func__, loop->name); | |||
158 | continue; | |||
159 | } | |||
160 | ||||
161 | /* | |||
162 | * If there are multiple clients attached, only accept the | |||
163 | * latest client; otherwise let the only client be chosen as | |||
164 | * for smallest. | |||
165 | */ | |||
166 | if (type == WINDOW_SIZE_LATEST3 && n > 1 && loop != w->latest) { | |||
167 | log_debug("%s: %s is not latest", __func__, loop->name); | |||
168 | continue; | |||
169 | } | |||
170 | ||||
171 | /* | |||
172 | * If the client has a per-window size, use this instead if it is | |||
173 | * smaller. | |||
174 | */ | |||
175 | if (w != NULL((void *)0)) | |||
176 | cw = server_client_get_client_window(loop, w->id); | |||
177 | else | |||
178 | cw = NULL((void *)0); | |||
179 | ||||
180 | /* Work out this client's size. */ | |||
181 | if (cw != NULL((void *)0) && cw->sx != 0 && cw->sy != 0) { | |||
182 | cx = cw->sx; | |||
183 | cy = cw->sy; | |||
184 | } else { | |||
185 | cx = loop->tty.sx; | |||
186 | cy = loop->tty.sy - status_line_size(loop); | |||
187 | } | |||
188 | ||||
189 | /* | |||
190 | * If it is larger or smaller than the best so far, update the | |||
191 | * new size. | |||
192 | */ | |||
193 | if (type == WINDOW_SIZE_LARGEST0) { | |||
194 | if (cx > *sx) | |||
195 | *sx = cx; | |||
196 | if (cy > *sy) | |||
197 | *sy = cy; | |||
198 | } else { | |||
199 | if (cx < *sx) | |||
200 | *sx = cx; | |||
201 | if (cy < *sy) | |||
202 | *sy = cy; | |||
203 | } | |||
204 | if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) { | |||
205 | *xpixel = loop->tty.xpixel; | |||
206 | *ypixel = loop->tty.ypixel; | |||
207 | } | |||
208 | log_debug("%s: after %s (%ux%u), size is %ux%u", __func__, | |||
209 | loop->name, cx, cy, *sx, *sy); | |||
210 | } | |||
211 | if (*sx != UINT_MAX(2147483647 *2U +1U) && *sy != UINT_MAX(2147483647 *2U +1U)) | |||
212 | log_debug("%s: calculated size %ux%u", __func__, *sx, *sy); | |||
213 | else | |||
214 | log_debug("%s: no calculated size", __func__); | |||
215 | ||||
216 | skip: | |||
217 | /* | |||
218 | * Do not allow any size to be larger than the per-client window size | |||
219 | * if one exists. | |||
220 | */ | |||
221 | if (w != NULL((void *)0)) { | |||
222 | TAILQ_FOREACH(loop, &clients, entry)for((loop) = ((&clients)->tqh_first); (loop) != ((void *)0); (loop) = ((loop)->entry.tqe_next)) { | |||
223 | if (loop != c && ignore_client_size(loop)) | |||
224 | continue; | |||
225 | if (loop != c && skip_client(loop, type, current, s, w)) | |||
226 | continue; | |||
227 | ||||
228 | /* Look up per-window size if any. */ | |||
229 | if (~loop->flags & CLIENT_WINDOWSIZECHANGED0x400000000ULL) | |||
230 | continue; | |||
231 | cw = server_client_get_client_window(loop, w->id); | |||
232 | if (cw == NULL((void *)0)) | |||
233 | continue; | |||
234 | ||||
235 | /* Clamp the size. */ | |||
236 | log_debug("%s: %s size for @%u is %ux%u", __func__, | |||
237 | loop->name, w->id, cw->sx, cw->sy); | |||
238 | if (cw->sx != 0 && *sx > cw->sx) | |||
239 | *sx = cw->sx; | |||
240 | if (cw->sy != 0 && *sy > cw->sy) | |||
241 | *sy = cw->sy; | |||
242 | } | |||
243 | } | |||
244 | if (*sx != UINT_MAX(2147483647 *2U +1U) && *sy != UINT_MAX(2147483647 *2U +1U)) | |||
245 | log_debug("%s: calculated size %ux%u", __func__, *sx, *sy); | |||
246 | else | |||
247 | log_debug("%s: no calculated size", __func__); | |||
248 | ||||
249 | /* Return whether a suitable size was found. */ | |||
250 | if (type == WINDOW_SIZE_MANUAL2) { | |||
251 | log_debug("%s: type is manual", __func__); | |||
252 | return (1); | |||
253 | } | |||
254 | if (type == WINDOW_SIZE_LARGEST0) { | |||
255 | log_debug("%s: type is largest", __func__); | |||
256 | return (*sx != 0 && *sy != 0); | |||
257 | } | |||
258 | if (type == WINDOW_SIZE_LATEST3) | |||
259 | log_debug("%s: type is latest", __func__); | |||
260 | else | |||
261 | log_debug("%s: type is smallest", __func__); | |||
262 | return (*sx != UINT_MAX(2147483647 *2U +1U) && *sy != UINT_MAX(2147483647 *2U +1U)); | |||
263 | } | |||
264 | ||||
265 | static int | |||
266 | default_window_size_skip_client(struct client *loop, int type, | |||
267 | __unused__attribute__((__unused__)) int current, struct session *s, struct window *w) | |||
268 | { | |||
269 | /* | |||
270 | * Latest checks separately, so do not check here. Otherwise only | |||
271 | * include clients where the session contains the window or where the | |||
272 | * session is the given session. | |||
273 | */ | |||
274 | if (type == WINDOW_SIZE_LATEST3) | |||
275 | return (0); | |||
276 | if (w != NULL((void *)0) && !session_has(loop->session, w)) | |||
277 | return (1); | |||
278 | if (w == NULL((void *)0) && loop->session != s) | |||
279 | return (1); | |||
280 | return (0); | |||
281 | } | |||
282 | ||||
283 | void | |||
284 | default_window_size(struct client *c, struct session *s, struct window *w, | |||
285 | u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) | |||
286 | { | |||
287 | const char *value; | |||
288 | ||||
289 | /* Get type if not provided. */ | |||
290 | if (type == -1) | |||
291 | type = options_get_number(global_w_options, "window-size"); | |||
292 | ||||
293 | /* | |||
294 | * Latest clients can use the given client if suitable. If there is no | |||
295 | * client and no window, use the default size as for manual type. | |||
296 | */ | |||
297 | if (type == WINDOW_SIZE_LATEST3 && c != NULL((void *)0) && !ignore_client_size(c)) { | |||
298 | *sx = c->tty.sx; | |||
299 | *sy = c->tty.sy - status_line_size(c); | |||
300 | *xpixel = c->tty.xpixel; | |||
301 | *ypixel = c->tty.ypixel; | |||
302 | log_debug("%s: using %ux%u from %s", __func__, *sx, *sy, | |||
303 | c->name); | |||
304 | goto done; | |||
305 | } | |||
306 | ||||
307 | /* | |||
308 | * Ignore the given client if it is a control client - the creating | |||
309 | * client should only affect the size if it is not a control client. | |||
310 | */ | |||
311 | if (c != NULL((void *)0) && (c->flags & CLIENT_CONTROL0x2000)) | |||
312 | c = NULL((void *)0); | |||
313 | ||||
314 | /* | |||
315 | * Look for a client to base the size on. If none exists (or the type | |||
316 | * is manual), use the default-size option. | |||
317 | */ | |||
318 | if (!clients_calculate_size(type, 0, c, s, w, | |||
319 | default_window_size_skip_client, sx, sy, xpixel, ypixel)) { | |||
320 | value = options_get_string(s->options, "default-size"); | |||
321 | if (sscanf(value, "%ux%u", sx, sy) != 2) { | |||
322 | *sx = 80; | |||
323 | *sy = 24; | |||
324 | } | |||
325 | log_debug("%s: using %ux%u from default-size", __func__, *sx, | |||
326 | *sy); | |||
327 | } | |||
328 | ||||
329 | done: | |||
330 | /* Make sure the limits are enforced. */ | |||
331 | if (*sx < WINDOW_MINIMUM1) | |||
332 | *sx = WINDOW_MINIMUM1; | |||
333 | if (*sx > WINDOW_MAXIMUM10000) | |||
334 | *sx = WINDOW_MAXIMUM10000; | |||
335 | if (*sy < WINDOW_MINIMUM1) | |||
336 | *sy = WINDOW_MINIMUM1; | |||
337 | if (*sy > WINDOW_MAXIMUM10000) | |||
338 | *sy = WINDOW_MAXIMUM10000; | |||
339 | log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy); | |||
340 | } | |||
341 | ||||
342 | static int | |||
343 | recalculate_size_skip_client(struct client *loop, __unused__attribute__((__unused__)) int type, | |||
344 | int current, __unused__attribute__((__unused__)) struct session *s, struct window *w) | |||
345 | { | |||
346 | /* | |||
347 | * If the current flag is set, then skip any client where this window | |||
348 | * is not the current window - this is used for aggressive-resize. | |||
349 | * Otherwise skip any session that doesn't contain the window. | |||
350 | */ | |||
351 | if (loop->session->curw == NULL((void *)0)) | |||
352 | return (1); | |||
353 | if (current) | |||
354 | return (loop->session->curw->window != w); | |||
355 | return (session_has(loop->session, w) == 0); | |||
356 | } | |||
357 | ||||
358 | void | |||
359 | recalculate_size(struct window *w, int now) | |||
360 | { | |||
361 | u_int sx, sy, xpixel = 0, ypixel = 0; | |||
362 | int type, current, changed; | |||
363 | ||||
364 | /* | |||
365 | * Do not attempt to resize windows which have no pane, they must be on | |||
366 | * the way to destruction. | |||
367 | */ | |||
368 | if (w->active == NULL((void *)0)) | |||
369 | return; | |||
370 | log_debug("%s: @%u is %ux%u", __func__, w->id, w->sx, w->sy); | |||
371 | ||||
372 | /* | |||
373 | * Type is manual, smallest, largest, latest. Current is the | |||
374 | * aggressive-resize option (do not resize based on clients where the | |||
375 | * window is not the current window). | |||
376 | */ | |||
377 | type = options_get_number(w->options, "window-size"); | |||
378 | current = options_get_number(w->options, "aggressive-resize"); | |||
379 | ||||
380 | /* Look for a suitable client and get the new size. */ | |||
381 | changed = clients_calculate_size(type, current, NULL((void *)0), NULL((void *)0), w, | |||
382 | recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); | |||
383 | ||||
384 | /* | |||
385 | * Make sure the size has actually changed. If the window has already | |||
386 | * got a resize scheduled, then use the new size; otherwise the old. | |||
387 | */ | |||
388 | if (w->flags & WINDOW_RESIZE0x20) { | |||
389 | if (!now && changed && w->new_sx == sx && w->new_sy == sy) | |||
390 | changed = 0; | |||
391 | } else { | |||
392 | if (!now && changed && w->sx == sx && w->sy == sy) | |||
393 | changed = 0; | |||
394 | } | |||
395 | ||||
396 | /* | |||
397 | * If the size hasn't changed, update the window offset but not the | |||
398 | * size. | |||
399 | */ | |||
400 | if (!changed) { | |||
401 | log_debug("%s: @%u no size change", __func__, w->id); | |||
402 | tty_update_window_offset(w); | |||
403 | return; | |||
404 | } | |||
405 | ||||
406 | /* | |||
407 | * If the now flag is set or if the window is sized manually, change | |||
408 | * the size immediately. Otherwise set the flag and it will be done | |||
409 | * later. | |||
410 | */ | |||
411 | log_debug("%s: @%u new size %ux%u", __func__, w->id, sx, sy); | |||
412 | if (now || type == WINDOW_SIZE_MANUAL2) | |||
413 | resize_window(w, sx, sy, xpixel, ypixel); | |||
414 | else { | |||
415 | w->new_sx = sx; | |||
416 | w->new_sy = sy; | |||
417 | w->new_xpixel = xpixel; | |||
418 | w->new_ypixel = ypixel; | |||
419 | ||||
420 | w->flags |= WINDOW_RESIZE0x20; | |||
421 | tty_update_window_offset(w); | |||
422 | } | |||
423 | } | |||
424 | ||||
425 | void | |||
426 | recalculate_sizes(void) | |||
427 | { | |||
428 | recalculate_sizes_now(0); | |||
| ||||
429 | } | |||
430 | ||||
431 | void | |||
432 | recalculate_sizes_now(int now) | |||
433 | { | |||
434 | struct session *s; | |||
435 | struct client *c; | |||
436 | struct window *w; | |||
437 | ||||
438 | /* | |||
439 | * Clear attached count and update saved status line information for | |||
440 | * each session. | |||
441 | */ | |||
442 | RB_FOREACH(s, sessions, &sessions)for ((s) = sessions_RB_MINMAX(&sessions, -1); (s) != ((void *)0); (s) = sessions_RB_NEXT(s)) { | |||
443 | s->attached = 0; | |||
444 | status_update_cache(s); | |||
445 | } | |||
446 | ||||
447 | /* | |||
448 | * Increment attached count and check the status line size for each | |||
449 | * client. | |||
450 | */ | |||
451 | TAILQ_FOREACH(c, &clients, entry)for((c) = ((&clients)->tqh_first); (c) != ((void *)0); (c) = ((c)->entry.tqe_next)) { | |||
452 | s = c->session; | |||
453 | if (s != NULL((void *)0) && !(c->flags & CLIENT_UNATTACHEDFLAGS(0x200| 0x40| 0x4))) | |||
454 | s->attached++; | |||
455 | if (ignore_client_size(c)) | |||
456 | continue; | |||
457 | if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL0x2000)) | |||
| ||||
458 | c->flags |= CLIENT_STATUSOFF0x800000; | |||
459 | else | |||
460 | c->flags &= ~CLIENT_STATUSOFF0x800000; | |||
461 | } | |||
462 | ||||
463 | /* Walk each window and adjust the size. */ | |||
464 | RB_FOREACH(w, windows, &windows)for ((w) = windows_RB_MINMAX(&windows, -1); (w) != ((void *)0); (w) = windows_RB_NEXT(w)) | |||
465 | recalculate_size(w, now); | |||
466 | } |