1 /* $OpenBSD: resize.c,v 1.38 2020/01/28 13:23:24 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_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