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