Home | History | Annotate | Line # | Download | only in dist
resize.c revision 1.4
      1  1.2  christos /* $OpenBSD$ */
      2  1.1      jmmv 
      3  1.1      jmmv /*
      4  1.2  christos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  1.1      jmmv  *
      6  1.1      jmmv  * Permission to use, copy, modify, and distribute this software for any
      7  1.1      jmmv  * purpose with or without fee is hereby granted, provided that the above
      8  1.1      jmmv  * copyright notice and this permission notice appear in all copies.
      9  1.1      jmmv  *
     10  1.1      jmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1      jmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1      jmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1      jmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1      jmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  1.1      jmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  1.1      jmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1      jmmv  */
     18  1.1      jmmv 
     19  1.1      jmmv #include <sys/types.h>
     20  1.1      jmmv 
     21  1.1      jmmv #include <string.h>
     22  1.1      jmmv 
     23  1.1      jmmv #include "tmux.h"
     24  1.1      jmmv 
     25  1.2  christos void
     26  1.2  christos resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
     27  1.2  christos {
     28  1.2  christos 	int	zoomed;
     29  1.2  christos 
     30  1.2  christos 	/* Check size limits. */
     31  1.2  christos 	if (sx < WINDOW_MINIMUM)
     32  1.2  christos 		sx = WINDOW_MINIMUM;
     33  1.2  christos 	if (sx > WINDOW_MAXIMUM)
     34  1.2  christos 		sx = WINDOW_MAXIMUM;
     35  1.2  christos 	if (sy < WINDOW_MINIMUM)
     36  1.2  christos 		sy = WINDOW_MINIMUM;
     37  1.2  christos 	if (sy > WINDOW_MAXIMUM)
     38  1.2  christos 		sy = WINDOW_MAXIMUM;
     39  1.2  christos 
     40  1.2  christos 	/* If the window is zoomed, unzoom. */
     41  1.2  christos 	zoomed = w->flags & WINDOW_ZOOMED;
     42  1.2  christos 	if (zoomed)
     43  1.2  christos 		window_unzoom(w);
     44  1.2  christos 
     45  1.2  christos 	/* Resize the layout first. */
     46  1.2  christos 	layout_resize(w, sx, sy);
     47  1.2  christos 
     48  1.2  christos 	/* Resize the window, it can be no smaller than the layout. */
     49  1.2  christos 	if (sx < w->layout_root->sx)
     50  1.2  christos 		sx = w->layout_root->sx;
     51  1.2  christos 	if (sy < w->layout_root->sy)
     52  1.2  christos 		sy = w->layout_root->sy;
     53  1.2  christos 	window_resize(w, sx, sy, xpixel, ypixel);
     54  1.2  christos 	log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id,
     55  1.2  christos 	    sx, sy, w->layout_root->sx, w->layout_root->sy);
     56  1.2  christos 
     57  1.2  christos 	/* Restore the window zoom state. */
     58  1.2  christos 	if (zoomed)
     59  1.2  christos 		window_zoom(w->active);
     60  1.2  christos 
     61  1.2  christos 	tty_update_window_offset(w);
     62  1.2  christos 	server_redraw_window(w);
     63  1.2  christos 	notify_window("window-layout-changed", w);
     64  1.3  christos 	w->flags &= ~WINDOW_RESIZE;
     65  1.2  christos }
     66  1.2  christos 
     67  1.2  christos static int
     68  1.2  christos ignore_client_size(struct client *c)
     69  1.2  christos {
     70  1.2  christos 	struct client	*loop;
     71  1.2  christos 
     72  1.2  christos 	if (c->session == NULL)
     73  1.2  christos 		return (1);
     74  1.2  christos 	if (c->flags & CLIENT_NOSIZEFLAGS)
     75  1.2  christos 		return (1);
     76  1.3  christos 	if (c->flags & CLIENT_IGNORESIZE) {
     77  1.2  christos 		/*
     78  1.3  christos 		 * Ignore flagged clients if there are any attached clients
     79  1.3  christos 		 * that aren't flagged.
     80  1.2  christos 		 */
     81  1.2  christos 		TAILQ_FOREACH (loop, &clients, entry) {
     82  1.2  christos 			if (loop->session == NULL)
     83  1.2  christos 				continue;
     84  1.2  christos 			if (loop->flags & CLIENT_NOSIZEFLAGS)
     85  1.2  christos 				continue;
     86  1.3  christos 			if (~loop->flags & CLIENT_IGNORESIZE)
     87  1.2  christos 				return (1);
     88  1.2  christos 		}
     89  1.2  christos 	}
     90  1.2  christos 	if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
     91  1.2  christos 		return (1);
     92  1.2  christos 	return (0);
     93  1.2  christos }
     94  1.1      jmmv 
     95  1.3  christos static u_int
     96  1.3  christos clients_with_window(struct window *w)
     97  1.3  christos {
     98  1.3  christos 	struct client	*loop;
     99  1.3  christos 	u_int		 n = 0;
    100  1.3  christos 
    101  1.3  christos 	TAILQ_FOREACH(loop, &clients, entry) {
    102  1.3  christos 		if (ignore_client_size(loop) || !session_has(loop->session, w))
    103  1.3  christos 			continue;
    104  1.3  christos 		if (++n > 1)
    105  1.3  christos 			break;
    106  1.3  christos 	}
    107  1.3  christos 	return (n);
    108  1.3  christos }
    109  1.3  christos 
    110  1.3  christos static int
    111  1.4  christos clients_calculate_size(int type, int current, struct client *c,
    112  1.4  christos     struct session *s, struct window *w, int (*skip_client)(struct client *,
    113  1.4  christos     int, int, struct session *, struct window *), u_int *sx, u_int *sy,
    114  1.4  christos     u_int *xpixel, u_int *ypixel)
    115  1.1      jmmv {
    116  1.2  christos 	struct client	*loop;
    117  1.3  christos 	u_int		 cx, cy, n = 0;
    118  1.3  christos 
    119  1.3  christos 	/* Manual windows do not have their size changed based on a client. */
    120  1.4  christos 	if (type == WINDOW_SIZE_MANUAL) {
    121  1.4  christos 		log_debug("%s: type is manual", __func__);
    122  1.3  christos 		return (0);
    123  1.4  christos 	}
    124  1.2  christos 
    125  1.3  christos 	/*
    126  1.3  christos 	 * Start comparing with 0 for largest and UINT_MAX for smallest or
    127  1.3  christos 	 * latest.
    128  1.3  christos 	 */
    129  1.3  christos 	if (type == WINDOW_SIZE_LARGEST)
    130  1.2  christos 		*sx = *sy = 0;
    131  1.3  christos 	else
    132  1.3  christos 		*sx = *sy = UINT_MAX;
    133  1.3  christos 	*xpixel = *ypixel = 0;
    134  1.3  christos 
    135  1.3  christos 	/*
    136  1.3  christos 	 * For latest, count the number of clients with this window. We only
    137  1.3  christos 	 * care if there is more than one.
    138  1.3  christos 	 */
    139  1.4  christos 	if (type == WINDOW_SIZE_LATEST && w != NULL)
    140  1.3  christos 		n = clients_with_window(w);
    141  1.3  christos 
    142  1.3  christos 	/* Loop over the clients and work out the size. */
    143  1.3  christos 	TAILQ_FOREACH(loop, &clients, entry) {
    144  1.4  christos 		if (loop != c && ignore_client_size(loop)) {
    145  1.4  christos 			log_debug("%s: ignoring %s", __func__, loop->name);
    146  1.3  christos 			continue;
    147  1.4  christos 		}
    148  1.4  christos 		if (loop != c && skip_client(loop, type, current, s, w)) {
    149  1.4  christos 			log_debug("%s: skipping %s", __func__, loop->name);
    150  1.3  christos 			continue;
    151  1.4  christos 		}
    152  1.3  christos 
    153  1.3  christos 		/*
    154  1.3  christos 		 * If there are multiple clients attached, only accept the
    155  1.3  christos 		 * latest client; otherwise let the only client be chosen as
    156  1.3  christos 		 * for smallest.
    157  1.3  christos 		 */
    158  1.4  christos 		if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) {
    159  1.4  christos 			log_debug("%s: %s is not latest", __func__, loop->name);
    160  1.3  christos 			continue;
    161  1.4  christos 		}
    162  1.2  christos 
    163  1.3  christos 		/* Work out this client's size. */
    164  1.3  christos 		cx = loop->tty.sx;
    165  1.3  christos 		cy = loop->tty.sy - status_line_size(loop);
    166  1.1      jmmv 
    167  1.3  christos 		/*
    168  1.3  christos 		 * If it is larger or smaller than the best so far, update the
    169  1.3  christos 		 * new size.
    170  1.3  christos 		 */
    171  1.3  christos 		if (type == WINDOW_SIZE_LARGEST) {
    172  1.2  christos 			if (cx > *sx)
    173  1.2  christos 				*sx = cx;
    174  1.2  christos 			if (cy > *sy)
    175  1.2  christos 				*sy = cy;
    176  1.3  christos 		} else {
    177  1.2  christos 			if (cx < *sx)
    178  1.2  christos 				*sx = cx;
    179  1.2  christos 			if (cy < *sy)
    180  1.2  christos 				*sy = cy;
    181  1.3  christos 		}
    182  1.3  christos 		if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
    183  1.3  christos 			*xpixel = loop->tty.xpixel;
    184  1.3  christos 			*ypixel = loop->tty.ypixel;
    185  1.3  christos 		}
    186  1.4  christos 		log_debug("%s: after %s (%ux%u), size is %ux%u", __func__,
    187  1.4  christos 		    loop->name, cx, cy, *sx, *sy);
    188  1.3  christos 	}
    189  1.3  christos 
    190  1.3  christos 	/* Return whether a suitable size was found. */
    191  1.4  christos 	if (type == WINDOW_SIZE_LARGEST) {
    192  1.4  christos 		log_debug("%s: type is largest", __func__);
    193  1.3  christos 		return (*sx != 0 && *sy != 0);
    194  1.4  christos 	}
    195  1.4  christos 	if (type == WINDOW_SIZE_LATEST)
    196  1.4  christos 		log_debug("%s: type is latest", __func__);
    197  1.4  christos 	else
    198  1.4  christos 		log_debug("%s: type is smallest", __func__);
    199  1.3  christos 	return (*sx != UINT_MAX && *sy != UINT_MAX);
    200  1.3  christos }
    201  1.3  christos 
    202  1.3  christos static int
    203  1.4  christos default_window_size_skip_client(struct client *loop, int type,
    204  1.3  christos     __unused int current, struct session *s, struct window *w)
    205  1.3  christos {
    206  1.3  christos 	/*
    207  1.3  christos 	 * Latest checks separately, so do not check here. Otherwise only
    208  1.3  christos 	 * include clients where the session contains the window or where the
    209  1.3  christos 	 * session is the given session.
    210  1.3  christos 	 */
    211  1.3  christos 	if (type == WINDOW_SIZE_LATEST)
    212  1.3  christos 		return (0);
    213  1.3  christos 	if (w != NULL && !session_has(loop->session, w))
    214  1.3  christos 		return (1);
    215  1.3  christos 	if (w == NULL && loop->session != s)
    216  1.3  christos 		return (1);
    217  1.3  christos 	return (0);
    218  1.3  christos }
    219  1.3  christos 
    220  1.3  christos void
    221  1.3  christos default_window_size(struct client *c, struct session *s, struct window *w,
    222  1.3  christos 	u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
    223  1.3  christos {
    224  1.3  christos 	const char	*value;
    225  1.3  christos 
    226  1.3  christos 	/* Get type if not provided. */
    227  1.3  christos 	if (type == -1)
    228  1.3  christos 		type = options_get_number(global_w_options, "window-size");
    229  1.2  christos 
    230  1.3  christos 	/*
    231  1.3  christos 	 * Latest clients can use the given client if suitable. If there is no
    232  1.3  christos 	 * client and no window, use the default size as for manual type.
    233  1.3  christos 	 */
    234  1.3  christos 	if (type == WINDOW_SIZE_LATEST) {
    235  1.2  christos 		if (c != NULL && !ignore_client_size(c)) {
    236  1.2  christos 			*sx = c->tty.sx;
    237  1.2  christos 			*sy = c->tty.sy - status_line_size(c);
    238  1.2  christos 			*xpixel = c->tty.xpixel;
    239  1.3  christos 			*ypixel = c->tty.ypixel;
    240  1.4  christos 			log_debug("%s: using %ux%u from %s", __func__, *sx, *sy,
    241  1.4  christos 			    c->name);
    242  1.3  christos 			goto done;
    243  1.1      jmmv 		}
    244  1.2  christos 	}
    245  1.1      jmmv 
    246  1.3  christos 	/*
    247  1.3  christos 	 * Look for a client to base the size on. If none exists (or the type
    248  1.3  christos 	 * is manual), use the default-size option.
    249  1.3  christos 	 */
    250  1.4  christos 	if (!clients_calculate_size(type, 0, c, s, w,
    251  1.3  christos 	    default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
    252  1.3  christos 		value = options_get_string(s->options, "default-size");
    253  1.3  christos 		if (sscanf(value, "%ux%u", sx, sy) != 2) {
    254  1.3  christos 			*sx = 80;
    255  1.3  christos 			*sy = 24;
    256  1.3  christos 		}
    257  1.4  christos 		log_debug("%s: using %ux%u from default-size", __func__, *sx,
    258  1.4  christos 		    *sy);
    259  1.1      jmmv 	}
    260  1.1      jmmv 
    261  1.2  christos done:
    262  1.3  christos 	/* Make sure the limits are enforced. */
    263  1.2  christos 	if (*sx < WINDOW_MINIMUM)
    264  1.2  christos 		*sx = WINDOW_MINIMUM;
    265  1.2  christos 	if (*sx > WINDOW_MAXIMUM)
    266  1.2  christos 		*sx = WINDOW_MAXIMUM;
    267  1.2  christos 	if (*sy < WINDOW_MINIMUM)
    268  1.2  christos 		*sy = WINDOW_MINIMUM;
    269  1.2  christos 	if (*sy > WINDOW_MAXIMUM)
    270  1.2  christos 		*sy = WINDOW_MAXIMUM;
    271  1.4  christos 	log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy);
    272  1.2  christos }
    273  1.1      jmmv 
    274  1.3  christos static int
    275  1.3  christos recalculate_size_skip_client(struct client *loop, __unused int type,
    276  1.3  christos     int current, __unused struct session *s, struct window *w)
    277  1.3  christos {
    278  1.3  christos 	/*
    279  1.3  christos 	 * If the current flag is set, then skip any client where this window
    280  1.3  christos 	 * is not the current window - this is used for aggressive-resize.
    281  1.3  christos 	 * Otherwise skip any session that doesn't contain the window.
    282  1.3  christos 	 */
    283  1.3  christos 	if (current)
    284  1.3  christos 		return (loop->session->curw->window != w);
    285  1.3  christos 	return (session_has(loop->session, w) == 0);
    286  1.3  christos }
    287  1.3  christos 
    288  1.2  christos void
    289  1.3  christos recalculate_size(struct window *w, int now)
    290  1.2  christos {
    291  1.3  christos 	u_int	sx, sy, xpixel = 0, ypixel = 0;
    292  1.3  christos 	int	type, current, changed;
    293  1.2  christos 
    294  1.3  christos 	/*
    295  1.3  christos 	 * Do not attempt to resize windows which have no pane, they must be on
    296  1.3  christos 	 * the way to destruction.
    297  1.3  christos 	 */
    298  1.2  christos 	if (w->active == NULL)
    299  1.2  christos 		return;
    300  1.2  christos 	log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
    301  1.2  christos 
    302  1.3  christos 	/*
    303  1.3  christos 	 * Type is manual, smallest, largest, latest. Current is the
    304  1.3  christos 	 * aggressive-resize option (do not resize based on clients where the
    305  1.3  christos 	 * window is not the current window).
    306  1.3  christos 	 */
    307  1.2  christos 	type = options_get_number(w->options, "window-size");
    308  1.2  christos 	current = options_get_number(w->options, "aggressive-resize");
    309  1.2  christos 
    310  1.3  christos 	/* Look for a suitable client and get the new size. */
    311  1.4  christos 	changed = clients_calculate_size(type, current, NULL, NULL, w,
    312  1.3  christos 	    recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
    313  1.2  christos 
    314  1.3  christos 	/*
    315  1.3  christos 	 * Make sure the size has actually changed. If the window has already
    316  1.3  christos 	 * got a resize scheduled, then use the new size; otherwise the old.
    317  1.3  christos 	 */
    318  1.3  christos 	if (w->flags & WINDOW_RESIZE) {
    319  1.3  christos 		if (!now && changed && w->new_sx == sx && w->new_sy == sy)
    320  1.2  christos 			changed = 0;
    321  1.3  christos 	} else {
    322  1.3  christos 		if (!now && changed && w->sx == sx && w->sy == sy)
    323  1.2  christos 			changed = 0;
    324  1.2  christos 	}
    325  1.1      jmmv 
    326  1.3  christos 	/*
    327  1.3  christos 	 * If the size hasn't changed, update the window offset but not the
    328  1.3  christos 	 * size.
    329  1.3  christos 	 */
    330  1.2  christos 	if (!changed) {
    331  1.2  christos 		tty_update_window_offset(w);
    332  1.2  christos 		return;
    333  1.1      jmmv 	}
    334  1.3  christos 
    335  1.3  christos 	/*
    336  1.3  christos 	 * If the now flag is set or if the window is sized manually, change
    337  1.3  christos 	 * the size immediately. Otherwise set the flag and it will be done
    338  1.3  christos 	 * later.
    339  1.3  christos 	 */
    340  1.3  christos 	log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
    341  1.3  christos 	if (now || type == WINDOW_SIZE_MANUAL)
    342  1.3  christos 		resize_window(w, sx, sy, xpixel, ypixel);
    343  1.3  christos 	else {
    344  1.3  christos 		w->new_sx = sx;
    345  1.3  christos 		w->new_sy = sy;
    346  1.3  christos 		w->new_xpixel = xpixel;
    347  1.3  christos 		w->new_ypixel = ypixel;
    348  1.3  christos 
    349  1.3  christos 		w->flags |= WINDOW_RESIZE;
    350  1.3  christos 		tty_update_window_offset(w);
    351  1.3  christos 	}
    352  1.2  christos }
    353  1.2  christos 
    354  1.2  christos void
    355  1.2  christos recalculate_sizes(void)
    356  1.2  christos {
    357  1.3  christos 	recalculate_sizes_now(0);
    358  1.3  christos }
    359  1.3  christos 
    360  1.3  christos void
    361  1.3  christos recalculate_sizes_now(int now)
    362  1.3  christos {
    363  1.2  christos 	struct session	*s;
    364  1.2  christos 	struct client	*c;
    365  1.2  christos 	struct window	*w;
    366  1.2  christos 
    367  1.2  christos 	/*
    368  1.2  christos 	 * Clear attached count and update saved status line information for
    369  1.2  christos 	 * each session.
    370  1.2  christos 	 */
    371  1.2  christos 	RB_FOREACH(s, sessions, &sessions) {
    372  1.2  christos 		s->attached = 0;
    373  1.2  christos 		status_update_cache(s);
    374  1.2  christos 	}
    375  1.2  christos 
    376  1.2  christos 	/*
    377  1.2  christos 	 * Increment attached count and check the status line size for each
    378  1.2  christos 	 * client.
    379  1.2  christos 	 */
    380  1.2  christos 	TAILQ_FOREACH(c, &clients, entry) {
    381  1.2  christos 		s = c->session;
    382  1.2  christos 		if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
    383  1.2  christos 			s->attached++;
    384  1.2  christos 		if (ignore_client_size(c))
    385  1.2  christos 			continue;
    386  1.2  christos 		if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
    387  1.2  christos 			c->flags |= CLIENT_STATUSOFF;
    388  1.2  christos 		else
    389  1.2  christos 			c->flags &= ~CLIENT_STATUSOFF;
    390  1.2  christos 	}
    391  1.2  christos 
    392  1.2  christos 	/* Walk each window and adjust the size. */
    393  1.2  christos 	RB_FOREACH(w, windows, &windows)
    394  1.3  christos 		recalculate_size(w, now);
    395  1.1      jmmv }
    396