mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Improved layout code.
Each window now has a tree of layout cells associated with it. In this tree, each node is either a horizontal or vertical cell containing a list of other cells running from left-to-right or top-to-bottom, or a leaf cell which is associated with a pane. The major functional changes are: - panes may now be split arbitrarily both horizontally (splitw -h, C-b %) and vertically (splitw -v, C-b "); - panes may be resized both horizontally and vertically (resizep -L/-R/-U/-D, bound to C-b left/right/up/down and C-b M-left/right/up/down); - layouts are now applied and then may be modified by resizing or splitting panes, rather than being fixed and reapplied when the window is resized or panes are added; - manual-vertical layout is no longer necessary, and active-only layout is gone (but may return in future); - the main-pane layouts now reduce the size of the main pane to fit all panes if possible. Thanks to all who tested.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -27,7 +27,7 @@ SRCS=	attributes.c buffer-poll.c buffer.c cfg.c client-fn.c \ | ||||
| 	cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \ | ||||
| 	cmd-up-pane.c cmd-display-message.c cmd.c \ | ||||
| 	colour.c grid-view.c grid.c input-keys.c \ | ||||
| 	input.c key-bindings.c key-string.c layout-manual.c layout.c log.c \ | ||||
| 	input.c key-bindings.c key-string.c layout-set.c layout.c log.c \ | ||||
| 	mode-key.c names.c options-cmd.c options.c paste.c procname.c \ | ||||
| 	resize.c screen-redraw.c screen-write.c screen.c server-fn.c \ | ||||
| 	server-msg.c server.c session.c status.c tmux.c tty-keys.c tty-term.c \ | ||||
|   | ||||
| @@ -74,17 +74,17 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		if (wl->window->active == NULL) | ||||
| 			wl->window->active = TAILQ_NEXT(wp, entry); | ||||
| 	} | ||||
|  	layout_refresh(wl->window, 0); | ||||
|  	layout_close_pane(wp); | ||||
|  | ||||
|  	w = wp->window = window_create1(s->sx, s->sy); | ||||
|  	TAILQ_INSERT_HEAD(&w->panes, wp, entry); | ||||
|  	w->active = wp; | ||||
|  	w->name = default_window_name(w); | ||||
| 	layout_init(w); | ||||
|  | ||||
|  	wl = session_attach(s, w, -1, &cause); /* can't fail */ | ||||
|  	if (!(data->chflags & CMD_CHFLAG('d'))) | ||||
|  		session_select(s, wl->idx); | ||||
|  	layout_refresh(w, 0); | ||||
|  | ||||
| 	server_redraw_session(s); | ||||
|  | ||||
|   | ||||
| @@ -76,8 +76,8 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		idx++; | ||||
|  | ||||
| 		window_choose_add(wl->window->active, | ||||
| 		    wm->idx, "%3d: %s [%ux%u %s] (%u panes)", wm->idx, w->name, | ||||
| 		    w->sx, w->sy, layout_name(w), window_count_panes(w)); | ||||
| 		    wm->idx, "%3d: %s [%ux%u] (%u panes)", | ||||
| 		    wm->idx, w->name, w->sx, w->sy, window_count_panes(w)); | ||||
| 	} | ||||
|  | ||||
| 	cdata = xmalloc(sizeof *cdata); | ||||
|   | ||||
| @@ -54,7 +54,6 @@ cmd_down_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		w->active = TAILQ_NEXT(w->active, entry); | ||||
| 		if (w->active == NULL) | ||||
| 			w->active = TAILQ_FIRST(&w->panes); | ||||
| 		layout_refresh(w, 1); | ||||
| 	} while (!window_pane_visible(w->active)); | ||||
|  | ||||
| 	return (0); | ||||
|   | ||||
| @@ -66,8 +66,8 @@ cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		return (0); | ||||
| 	} | ||||
|  | ||||
| 	layout_close_pane(wp); | ||||
| 	window_remove_pane(wl->window, wp); | ||||
| 	server_redraw_window(wl->window); | ||||
| 	layout_refresh(wl->window, 0); | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -78,9 +78,8 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 			else | ||||
| 				name = "unknown"; | ||||
| 			ctx->print(ctx, | ||||
| 			    "     %s [%ux%u %s] [history %u/%u, %llu bytes]", | ||||
| 			    name, wp->sx, wp->sy, layout_name(w), gd->hsize, | ||||
| 			    gd->hlimit, size); | ||||
| 			    "     %s [%ux%u] [history %u/%u, %llu bytes]", | ||||
| 			    name, wp->sx, wp->sy, gd->hsize, gd->hlimit, size); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -44,12 +44,13 @@ cmd_next_layout_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| { | ||||
| 	struct cmd_target_data	*data = self->data; | ||||
| 	struct winlink		*wl; | ||||
| 	u_int			 layout; | ||||
|  | ||||
| 	if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) | ||||
| 		return (-1); | ||||
|  | ||||
| 	layout_next(wl->window); | ||||
| 	ctx->info(ctx, "layout now: %s", layout_name(wl->window)); | ||||
| 	layout = layout_set_next(wl->window); | ||||
| 	ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -44,12 +44,13 @@ cmd_previous_layout_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| { | ||||
| 	struct cmd_target_data	*data = self->data; | ||||
| 	struct winlink		*wl; | ||||
|  | ||||
| 	u_int			 layout; | ||||
| 	 | ||||
| 	if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) | ||||
| 		return (-1); | ||||
|  | ||||
| 	layout_previous(wl->window); | ||||
| 	ctx->info(ctx, "layout now: %s", layout_name(wl->window)); | ||||
| 	layout = layout_set_previous(wl->window); | ||||
| 	ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -32,7 +32,8 @@ int	cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *); | ||||
| const struct cmd_entry cmd_resize_pane_entry = { | ||||
| 	"resize-pane", "resizep", | ||||
| 	CMD_PANE_WINDOW_USAGE "[-DU] [adjustment]", | ||||
| 	CMD_ARG01, CMD_CHFLAG('D')|CMD_CHFLAG('U'), | ||||
| 	CMD_ARG01, | ||||
| 	CMD_CHFLAG('D')|CMD_CHFLAG('L')|CMD_CHFLAG('R')|CMD_CHFLAG('U'), | ||||
| 	cmd_resize_pane_init, | ||||
| 	cmd_pane_parse, | ||||
| 	cmd_resize_pane_exec, | ||||
| @@ -50,15 +51,31 @@ cmd_resize_pane_init(struct cmd *self, int key) | ||||
| 	cmd_pane_init(self, key); | ||||
| 	data = self->data; | ||||
|  | ||||
| 	if (key == KEYC_ADDCTL(KEYC_UP)) | ||||
| 		data->chflags |= CMD_CHFLAG('U'); | ||||
| 	if (key == KEYC_ADDCTL(KEYC_DOWN)) | ||||
| 		data->chflags |= CMD_CHFLAG('D'); | ||||
| 	if (key == KEYC_ADDCTL(KEYC_LEFT)) | ||||
| 		data->chflags |= CMD_CHFLAG('L'); | ||||
| 	if (key == KEYC_ADDCTL(KEYC_RIGHT)) | ||||
| 		data->chflags |= CMD_CHFLAG('R'); | ||||
|  | ||||
| 	if (key == KEYC_ADDESC(KEYC_UP)) | ||||
| 	if (key == KEYC_ADDESC(KEYC_UP)) { | ||||
| 		data->chflags |= CMD_CHFLAG('U'); | ||||
| 		data->arg = xstrdup("5"); | ||||
| 	} | ||||
| 	if (key == KEYC_ADDESC(KEYC_DOWN)) { | ||||
| 		data->chflags |= CMD_CHFLAG('D'); | ||||
| 		data->arg = xstrdup("5"); | ||||
| 	} | ||||
| 	if (key == KEYC_ADDESC(KEYC_LEFT)) { | ||||
| 		data->chflags |= CMD_CHFLAG('L'); | ||||
| 		data->arg = xstrdup("5"); | ||||
| 	} | ||||
| 	if (key == KEYC_ADDESC(KEYC_RIGHT)) { | ||||
| 		data->chflags |= CMD_CHFLAG('R'); | ||||
| 		data->arg = xstrdup("5"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int | ||||
| @@ -92,12 +109,14 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(data->chflags & CMD_CHFLAG('D'))) | ||||
| 		adjust = -adjust; | ||||
| 	if (layout_resize(wp, adjust) != 0) { | ||||
| 		ctx->error(ctx, "layout %s " | ||||
| 		    "does not support resizing", layout_name(wp->window)); | ||||
| 		return (-1); | ||||
| 	if (data->chflags & (CMD_CHFLAG('L')|CMD_CHFLAG('R'))) { | ||||
| 		if (data->chflags & CMD_CHFLAG('L')) | ||||
| 			adjust = -adjust; | ||||
| 		layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust); | ||||
| 	} else { | ||||
| 		if (data->chflags & CMD_CHFLAG('U')) | ||||
| 			adjust = -adjust; | ||||
| 		layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust); | ||||
| 	} | ||||
| 	server_redraw_window(wl->window); | ||||
|  | ||||
|   | ||||
| @@ -70,6 +70,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
|  | ||||
| 	wp = TAILQ_FIRST(&w->panes); | ||||
| 	TAILQ_REMOVE(&w->panes, wp, entry); | ||||
| 	layout_free(w); | ||||
|  	window_destroy_panes(w); | ||||
| 	TAILQ_INSERT_HEAD(&w->panes, wp, entry); | ||||
| 	window_pane_resize(wp, w->sx, w->sy); | ||||
| @@ -78,6 +79,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		xfree(cause); | ||||
| 		return (-1); | ||||
| 	} | ||||
| 	layout_init(w); | ||||
| 	screen_reinit(&wp->base); | ||||
|  | ||||
| 	recalculate_sizes(); | ||||
|   | ||||
| @@ -59,6 +59,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	struct winlink		*wl; | ||||
| 	struct window		*w; | ||||
| 	struct window_pane	*wp, *wp2; | ||||
| 	struct layout_cell	*lc; | ||||
| 	u_int			 sx, sy, xoff, yoff; | ||||
|  | ||||
| 	if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) | ||||
| @@ -70,42 +71,56 @@ cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		TAILQ_REMOVE(&w->panes, wp, entry); | ||||
| 		TAILQ_INSERT_HEAD(&w->panes, wp, entry); | ||||
|  | ||||
| 		lc = wp->layout_cell; | ||||
| 		xoff = wp->xoff; yoff = wp->yoff; | ||||
| 		sx = wp->sx; sy = wp->sy; | ||||
| 		TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 			if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL) | ||||
| 				break; | ||||
| 			wp->layout_cell = wp2->layout_cell; | ||||
| 			if (wp->layout_cell != NULL) | ||||
| 				wp->layout_cell->wp = wp; | ||||
| 			wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; | ||||
| 			window_pane_resize(wp, wp2->sx, wp2->sy); | ||||
| 		} | ||||
| 		wp->layout_cell = lc; | ||||
| 		if (wp->layout_cell != NULL) | ||||
| 			wp->layout_cell->wp = wp; | ||||
| 		wp->xoff = xoff; wp->yoff = yoff; | ||||
| 		window_pane_resize(wp, sx, sy); | ||||
|  | ||||
| 		if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) | ||||
| 			wp = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		window_set_active_pane(w, wp); | ||||
| 		server_redraw_window(w); | ||||
| 	} else { | ||||
| 		wp = TAILQ_FIRST(&w->panes); | ||||
| 		TAILQ_REMOVE(&w->panes, wp, entry); | ||||
| 		TAILQ_INSERT_TAIL(&w->panes, wp, entry); | ||||
|  | ||||
| 		lc = wp->layout_cell; | ||||
| 		xoff = wp->xoff; yoff = wp->yoff; | ||||
| 		sx = wp->sx; sy = wp->sy; | ||||
| 		TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) { | ||||
| 			if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL) | ||||
| 				break; | ||||
| 			wp->layout_cell = wp2->layout_cell; | ||||
| 			if (wp->layout_cell != NULL) | ||||
| 				wp->layout_cell->wp = wp; | ||||
| 			wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; | ||||
| 			window_pane_resize(wp, wp2->sx, wp2->sy); | ||||
| 		} | ||||
| 		wp->layout_cell = lc; | ||||
| 		if (wp->layout_cell != NULL) | ||||
| 			wp->layout_cell->wp = wp; | ||||
| 		wp->xoff = xoff; wp->yoff = yoff; | ||||
| 		window_pane_resize(wp, sx, sy); | ||||
|  | ||||
| 		if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) | ||||
| 			wp = TAILQ_FIRST(&w->panes); | ||||
| 		window_set_active_pane(w, wp); | ||||
| 		server_redraw_window(w); | ||||
| 	} | ||||
|  | ||||
| 	layout_refresh(w, 0); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -49,17 +49,17 @@ cmd_select_layout_init(struct cmd *self, int key) | ||||
| 	data = self->data; | ||||
|  | ||||
| 	switch (key) { | ||||
| 	case KEYC_ADDESC('0'): | ||||
| 		data->arg = xstrdup("manual-vertical"); | ||||
| 		break; | ||||
| 	case KEYC_ADDESC('1'): | ||||
| 		data->arg = xstrdup("even-horizontal"); | ||||
| 		break; | ||||
| 	case KEYC_ADDESC('2'): | ||||
| 		data->arg = xstrdup("even-vertical"); | ||||
| 		break; | ||||
| 	case KEYC_ADDESC('9'): | ||||
| 		data->arg = xstrdup("active-only"); | ||||
| 	case KEYC_ADDESC('3'): | ||||
| 		data->arg = xstrdup("main-horizontal"); | ||||
| 		break; | ||||
| 	case KEYC_ADDESC('4'): | ||||
| 		data->arg = xstrdup("main-vertical"); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| @@ -74,13 +74,13 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL) | ||||
| 		return (-1); | ||||
|  | ||||
| 	if ((layout = layout_lookup(data->arg)) == -1) { | ||||
| 		ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg); | ||||
| 		return (-1); | ||||
| 	} | ||||
| 	if ((layout = layout_set_lookup(data->arg)) == -1) { | ||||
|  		ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg); | ||||
|  		return (-1); | ||||
|  	} | ||||
|  | ||||
| 	if (layout_select(wl->window, layout) == 0) | ||||
| 		ctx->info(ctx, "layout now: %s", layout_name(wl->window)); | ||||
| 	layout = layout_set_select(wl->window, layout); | ||||
| 	ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -63,7 +63,6 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		return (-1); | ||||
| 	} | ||||
| 	window_set_active_pane(wl->window, wp); | ||||
| 	layout_refresh(wl->window, 1); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -39,13 +39,14 @@ struct cmd_split_window_data { | ||||
| 	char	*target; | ||||
| 	char	*cmd; | ||||
| 	int	 flag_detached; | ||||
| 	int	 flag_horizontal; | ||||
| 	int	 percentage; | ||||
| 	int	 lines; | ||||
| 	int	 size; | ||||
| }; | ||||
|  | ||||
| const struct cmd_entry cmd_split_window_entry = { | ||||
| 	"split-window", "splitw", | ||||
| 	"[-d] [-p percentage|-l lines] [-t target-window] [command]", | ||||
| 	"[-dhv] [-p percentage|-l size] [-t target-window] [command]", | ||||
| 	0, 0, | ||||
| 	cmd_split_window_init, | ||||
| 	cmd_split_window_parse, | ||||
| @@ -57,7 +58,7 @@ const struct cmd_entry cmd_split_window_entry = { | ||||
| }; | ||||
|  | ||||
| void | ||||
| cmd_split_window_init(struct cmd *self, unused int arg) | ||||
| cmd_split_window_init(struct cmd *self, int key) | ||||
| { | ||||
| 	struct cmd_split_window_data	 *data; | ||||
|  | ||||
| @@ -65,50 +66,63 @@ cmd_split_window_init(struct cmd *self, unused int arg) | ||||
| 	data->target = NULL; | ||||
| 	data->cmd = NULL; | ||||
| 	data->flag_detached = 0; | ||||
| 	data->flag_horizontal = 0; | ||||
| 	data->percentage = -1; | ||||
| 	data->lines = -1; | ||||
| 	data->size = -1; | ||||
|  | ||||
| 	switch (key) { | ||||
| 	case '%': | ||||
| 		data->flag_horizontal = 1; | ||||
| 		break;		 | ||||
| 	case '"': | ||||
| 		data->flag_horizontal = 0; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int | ||||
| cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause) | ||||
| { | ||||
| 	struct cmd_split_window_data	*data; | ||||
| 	int				 opt, n; | ||||
| 	int				 opt; | ||||
| 	const char			*errstr; | ||||
|  | ||||
| 	self->entry->init(self, 0); | ||||
| 	data = self->data; | ||||
|  | ||||
| 	while ((opt = getopt(argc, argv, "dl:p:t:")) != -1) { | ||||
| 	while ((opt = getopt(argc, argv, "dhl:p:t:v")) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case 'd': | ||||
| 			data->flag_detached = 1; | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			data->flag_horizontal = 1; | ||||
| 			break; | ||||
| 		case 't': | ||||
| 			if (data->target == NULL) | ||||
| 				data->target = xstrdup(optarg); | ||||
| 			break; | ||||
| 		case 'l': | ||||
| 			if (data->percentage == -1 && data->lines == -1) { | ||||
| 				n = strtonum(optarg, 1, INT_MAX, &errstr); | ||||
| 				if (errstr != NULL) { | ||||
| 					xasprintf(cause, "lines %s", errstr); | ||||
| 					goto error; | ||||
| 				} | ||||
| 				data->lines = n; | ||||
| 			if (data->percentage != -1 || data->size != -1) | ||||
| 				break; | ||||
| 			data->size = strtonum(optarg, 1, INT_MAX, &errstr); | ||||
| 			if (errstr != NULL) { | ||||
| 				xasprintf(cause, "size %s", errstr); | ||||
| 				goto error; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			if (data->lines == -1 && data->percentage == -1) { | ||||
| 				n = strtonum(optarg, 1, 100, &errstr); | ||||
| 				if (errstr != NULL) { | ||||
| 					xasprintf( | ||||
| 					    cause, "percentage %s", errstr); | ||||
| 					goto error; | ||||
| 				} | ||||
| 				data->percentage = n; | ||||
| 			if (data->size != -1 || data->percentage != -1) | ||||
| 				break; | ||||
| 			data->percentage = strtonum(optarg, 1, 100, &errstr); | ||||
| 			if (errstr != NULL) { | ||||
| 				xasprintf(cause, "percentage %s", errstr); | ||||
| 				goto error; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			data->flag_horizontal = 0; | ||||
| 			break; | ||||
| 		default: | ||||
| 			goto usage; | ||||
| 		} | ||||
| @@ -142,7 +156,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	const char		       **env; | ||||
| 	char		 		*cmd, *cwd, *cause; | ||||
| 	u_int				 hlimit; | ||||
| 	int				 lines; | ||||
| 	int				 size; | ||||
| 	enum layout_type		 type; | ||||
|  | ||||
| 	if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL) | ||||
| 		return (-1); | ||||
| @@ -158,19 +173,27 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	else | ||||
| 		cwd = ctx->cmdclient->cwd; | ||||
|  | ||||
| 	lines = -1; | ||||
| 	if (data->lines != -1) | ||||
| 		lines = data->lines; | ||||
| 	size = -1; | ||||
| 	if (data->size != -1) | ||||
| 		size = data->size; | ||||
| 	else if (data->percentage != -1) | ||||
| 		lines = (w->active->sy * data->percentage) / 100; | ||||
|  | ||||
| 		size = (w->active->sy * data->percentage) / 100; | ||||
| 	hlimit = options_get_number(&s->options, "history-limit"); | ||||
| 	wp = window_add_pane(w, lines, cmd, cwd, env, hlimit, &cause); | ||||
| 	if (wp == NULL) { | ||||
| 		ctx->error(ctx, "create pane failed: %s", cause); | ||||
| 		xfree(cause); | ||||
| 		return (-1); | ||||
|  | ||||
| 	type = LAYOUT_TOPBOTTOM; | ||||
| 	if (data->flag_horizontal) | ||||
| 		type = LAYOUT_LEFTRIGHT; | ||||
|  | ||||
| 	wp = window_add_pane(w, hlimit, &cause); | ||||
| 	if (wp == NULL) | ||||
| 		goto error; | ||||
| 	if (window_pane_spawn(wp, cmd, cwd, env, &cause) != 0) | ||||
| 		goto error; | ||||
| 	if (layout_split_pane(w->active, type, size, wp) != 0) { | ||||
| 		cause = xstrdup("pane too small"); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	server_redraw_window(w); | ||||
|  | ||||
| 	if (!data->flag_detached) { | ||||
| @@ -179,9 +202,15 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		server_redraw_session(s); | ||||
| 	} else | ||||
| 		server_status_session(s); | ||||
| 	layout_refresh(w, 0); | ||||
|  | ||||
| 	return (0); | ||||
|  | ||||
| error: | ||||
| 	if (wp != NULL) | ||||
| 		window_remove_pane(w, wp); | ||||
| 	ctx->error(ctx, "create pane failed: %s", cause); | ||||
| 	xfree(cause); | ||||
| 	return (-1); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -228,6 +257,8 @@ cmd_split_window_print(struct cmd *self, char *buf, size_t len) | ||||
| 		return (off); | ||||
| 	if (off < len && data->flag_detached) | ||||
| 		off += xsnprintf(buf + off, len - off, " -d"); | ||||
| 	if (off < len && data->flag_horizontal) | ||||
| 		off += xsnprintf(buf + off, len - off, " -h"); | ||||
| 	if (off < len && data->target != NULL) | ||||
| 		off += cmd_prarg(buf + off, len - off, " -t ", data->target); | ||||
| 	if (off < len && data->cmd != NULL) | ||||
|   | ||||
| @@ -157,6 +157,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	struct winlink			*wl; | ||||
| 	struct window			*w; | ||||
| 	struct window_pane		*tmp_wp, *src_wp, *dst_wp; | ||||
| 	struct layout_cell		*lc; | ||||
| 	u_int				 xx, yy; | ||||
|  | ||||
| 	if (data == NULL) | ||||
| @@ -207,12 +208,15 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 	else | ||||
| 		TAILQ_INSERT_AFTER(&w->panes, tmp_wp, src_wp, entry); | ||||
|  | ||||
| 	lc = src_wp->layout_cell; | ||||
| 	xx = src_wp->xoff; | ||||
| 	yy = src_wp->yoff; | ||||
|  	src_wp->xoff = dst_wp->xoff; | ||||
|  	src_wp->yoff = dst_wp->yoff; | ||||
|  	dst_wp->xoff = xx; | ||||
|  	dst_wp->yoff = yy; | ||||
| 	src_wp->layout_cell = dst_wp->layout_cell; | ||||
| 	if (src_wp->layout_cell != NULL) | ||||
| 		src_wp->layout_cell->wp = src_wp; | ||||
| 	dst_wp->layout_cell = lc; | ||||
| 	if (dst_wp->layout_cell != NULL) | ||||
| 		dst_wp->layout_cell->wp = dst_wp; | ||||
|  | ||||
| 	xx = src_wp->sx; | ||||
| 	yy = src_wp->sy; | ||||
| @@ -224,8 +228,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		if (!window_pane_visible(tmp_wp)) | ||||
| 			tmp_wp = src_wp; | ||||
| 		window_set_active_pane(w, tmp_wp); | ||||
| 		layout_refresh(w, 0); | ||||
| 	} | ||||
| 	server_redraw_window(w); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|   | ||||
| @@ -54,7 +54,6 @@ cmd_up_pane_exec(struct cmd *self, struct cmd_ctx *ctx) | ||||
| 		w->active = TAILQ_PREV(w->active, window_panes, entry); | ||||
| 		if (w->active == NULL) | ||||
| 			w->active = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		layout_refresh(w, 1); | ||||
| 	} while (!window_pane_visible(w->active)); | ||||
|  | ||||
| 	return (0); | ||||
|   | ||||
| @@ -93,7 +93,8 @@ key_bindings_init(void) | ||||
| 	} table[] = { | ||||
| 		{ ' ',			  0, &cmd_next_layout_entry }, | ||||
| 		{ '!', 			  0, &cmd_break_pane_entry }, | ||||
| 		{ '"', 			  0, &cmd_split_window_entry }, | ||||
| 		{ '"', 			  0, &cmd_split_window_entry },	 | ||||
| 		{ '%', 			  0, &cmd_split_window_entry },	 | ||||
| 		{ '#', 			  0, &cmd_list_buffers_entry }, | ||||
| 		{ '&', 			  0, &cmd_confirm_before_entry }, | ||||
| 		{ ',', 			  0, &cmd_command_prompt_entry }, | ||||
| @@ -132,10 +133,10 @@ key_bindings_init(void) | ||||
| 		{ '{',			  0, &cmd_swap_pane_entry }, | ||||
| 		{ '}',			  0, &cmd_swap_pane_entry }, | ||||
| 		{ '\002', 		  0, &cmd_send_prefix_entry }, | ||||
| 		{ KEYC_ADDESC('0'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_ADDESC('1'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_ADDESC('2'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_ADDESC('9'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_ADDESC('3'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_ADDESC('4'),	  0, &cmd_select_layout_entry }, | ||||
| 		{ KEYC_PPAGE, 		  0, &cmd_scroll_mode_entry }, | ||||
| 		{ KEYC_ADDESC('n'), 	  0, &cmd_next_window_entry }, | ||||
| 		{ KEYC_ADDESC('p'), 	  0, &cmd_previous_window_entry }, | ||||
| @@ -143,8 +144,12 @@ key_bindings_init(void) | ||||
| 		{ KEYC_DOWN, 		  0, &cmd_down_pane_entry }, | ||||
| 		{ KEYC_ADDESC(KEYC_UP),   1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDESC(KEYC_DOWN), 1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDESC(KEYC_LEFT), 1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDESC(KEYC_RIGHT),1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDCTL(KEYC_UP),   1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry },	 | ||||
| 		{ KEYC_ADDCTL(KEYC_LEFT), 1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDCTL(KEYC_RIGHT),1, &cmd_resize_pane_entry }, | ||||
| 		{ KEYC_ADDESC('o'),	  0, &cmd_rotate_window_entry }, | ||||
| 		{ '\017',	          0, &cmd_rotate_window_entry }, | ||||
| 	}; | ||||
|   | ||||
							
								
								
									
										172
									
								
								layout-manual.c
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								layout-manual.c
									
									
									
									
									
								
							| @@ -1,172 +0,0 @@ | ||||
| /* $OpenBSD$ */ | ||||
|  | ||||
| /* | ||||
|  * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| void	layout_manual_v_update_offsets(struct window *); | ||||
|  | ||||
| void | ||||
| layout_manual_v_refresh(struct window *w, unused int active_only) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 npanes, total, height; | ||||
| 	int			 left; | ||||
|  | ||||
| 	if (active_only) | ||||
| 		return; | ||||
|  | ||||
| 	if (TAILQ_EMPTY(&w->panes)) | ||||
| 		return; | ||||
|  | ||||
| 	/* Check the new size. */ | ||||
| 	npanes = window_count_panes(w); | ||||
| 	if (w->sy <= PANE_MINIMUM * npanes) { | ||||
| 		/*  | ||||
| 		 * Make the first pane the smaller of the minimum and total (it | ||||
| 		 * must fit to be visible) and the rest the minimum size. | ||||
| 		 */ | ||||
| 		height = PANE_MINIMUM; | ||||
| 		if (height > w->sy) | ||||
| 			height = w->sy + 1; | ||||
| 		TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 			if (wp == TAILQ_FIRST(&w->panes)) | ||||
| 				wp->sy = height - 1; | ||||
| 			else | ||||
| 				wp->sy = PANE_MINIMUM - 1; | ||||
| 		} | ||||
| 		/* And increase the first by the rest if possible. */ | ||||
| 		if (w->sy >= PANE_MINIMUM) | ||||
| 			TAILQ_FIRST(&w->panes)->sy += 1 + w->sy % PANE_MINIMUM; | ||||
| 	} else { | ||||
| 		/* In theory they will all fit. Find the current total. */ | ||||
| 		total = 0; | ||||
| 		TAILQ_FOREACH(wp, &w->panes, entry) | ||||
| 			total += wp->sy; | ||||
| 		total += npanes - 1; | ||||
|  | ||||
| 		/* Growing or shrinking? */ | ||||
| 		left = w->sy - total; | ||||
| 		if (left > 0) { | ||||
| 			/* Growing. Expand evenly. */ | ||||
| 			while (left > 0) { | ||||
| 				TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 					wp->sy++; | ||||
| 					if (--left == 0) | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			/* Shrinking. Reduce evenly down to minimum. */ | ||||
| 			while (left < 0) { | ||||
| 				TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 					if (wp->sy <= PANE_MINIMUM - 1) | ||||
| 						continue; | ||||
| 					wp->sy--; | ||||
| 					if (++left == 0) | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Now do the resize. */ | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		wp->sy--; | ||||
| 	    	window_pane_resize(wp, w->sx, wp->sy + 1); | ||||
| 	} | ||||
|  | ||||
| 	/* Fill in the offsets. */ | ||||
| 	layout_manual_v_update_offsets(w); | ||||
|  | ||||
| 	/* Switch the active window if necessary. */ | ||||
| 	window_set_active_pane(w, w->active); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_manual_v_resize(struct window_pane *wp, int adjust) | ||||
| { | ||||
| 	struct window		*w = wp->window; | ||||
| 	struct window_pane	*wq; | ||||
|  | ||||
| 	if (adjust > 0) { | ||||
| 		/* | ||||
| 		 * If this is not the last pane, keep trying to increase size | ||||
| 		 * and remove it from the next panes. If it is the last, do | ||||
| 		 * so on the previous pane. | ||||
| 		 */ | ||||
| 		if (TAILQ_NEXT(wp, entry) == NULL) { | ||||
| 			if (wp == TAILQ_FIRST(&w->panes)) { | ||||
| 				/* Only one pane. */ | ||||
| 				return; | ||||
| 			} | ||||
| 			wp = TAILQ_PREV(wp, window_panes, entry); | ||||
| 		} | ||||
| 		while (adjust-- > 0) { | ||||
| 			wq = wp; | ||||
| 			while ((wq = TAILQ_NEXT(wq, entry)) != NULL) { | ||||
| 				if (wq->sy <= PANE_MINIMUM) | ||||
| 					continue; | ||||
| 				window_pane_resize(wq, wq->sx, wq->sy - 1); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (wq == NULL) | ||||
| 				break; | ||||
| 			window_pane_resize(wp, wp->sx, wp->sy + 1); | ||||
| 		} | ||||
| 	} else { | ||||
| 		adjust = -adjust; | ||||
| 		/* | ||||
| 		 * If this is not the last pane, keep trying to reduce size | ||||
| 		 * and add to the following pane. If it is the last, do so on | ||||
| 		 * the previous pane. | ||||
| 		 */ | ||||
| 		wq = TAILQ_NEXT(wp, entry); | ||||
| 		if (wq == NULL) { | ||||
| 			if (wp == TAILQ_FIRST(&w->panes)) { | ||||
| 				/* Only one pane. */ | ||||
| 				return; | ||||
| 			} | ||||
| 			wq = wp; | ||||
| 			wp = TAILQ_PREV(wq, window_panes, entry); | ||||
| 		} | ||||
| 		while (adjust-- > 0) { | ||||
| 			if (wp->sy <= PANE_MINIMUM) | ||||
| 				break; | ||||
| 			window_pane_resize(wq, wq->sx, wq->sy + 1); | ||||
| 			window_pane_resize(wp, wp->sx, wp->sy - 1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	layout_manual_v_update_offsets(w); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_manual_v_update_offsets(struct window *w) | ||||
| { | ||||
| 	struct window_pane     *wp; | ||||
| 	u_int			yoff; | ||||
|  | ||||
| 	yoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		wp->xoff = 0; | ||||
| 		wp->yoff = yoff; | ||||
| 		yoff += wp->sy + 1; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										436
									
								
								layout-set.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								layout-set.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,436 @@ | ||||
| /* $OpenBSD$ */ | ||||
|  | ||||
| /* | ||||
|  * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| /* | ||||
|  * Set window layouts - predefined methods to arrange windows. These are one-off | ||||
|  * and generate a layout tree. | ||||
|  */ | ||||
|  | ||||
| void	layout_set_even_h(struct window *); | ||||
| void	layout_set_even_v(struct window *); | ||||
| void	layout_set_main_h(struct window *); | ||||
| void	layout_set_main_v(struct window *); | ||||
|  | ||||
| const struct { | ||||
| 	const char	*name; | ||||
| 	void	      	(*arrange)(struct window *); | ||||
| } layout_sets[] = { | ||||
| 	{ "even-horizontal", layout_set_even_h }, | ||||
| 	{ "even-vertical", layout_set_even_v }, | ||||
| 	{ "main-horizontal", layout_set_main_h }, | ||||
| 	{ "main-vertical", layout_set_main_v }, | ||||
| }; | ||||
|  | ||||
| const char * | ||||
| layout_set_name(u_int layout) | ||||
| { | ||||
| 	return (layout_sets[layout].name); | ||||
| } | ||||
|  | ||||
| int | ||||
| layout_set_lookup(const char *name) | ||||
| { | ||||
| 	u_int	i; | ||||
| 	int	matched = -1; | ||||
|  | ||||
| 	for (i = 0; i < nitems(layout_sets); i++) { | ||||
| 		if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) { | ||||
| 			if (matched != -1)	/* ambiguous */ | ||||
| 				return (-1); | ||||
| 			matched = i; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return (matched); | ||||
| } | ||||
|  | ||||
| u_int | ||||
| layout_set_select(struct window *w, u_int layout) | ||||
| { | ||||
| 	if (layout > nitems(layout_sets) - 1) | ||||
| 		layout = nitems(layout_sets) - 1; | ||||
|  | ||||
| 	if (layout_sets[layout].arrange != NULL) | ||||
| 		layout_sets[layout].arrange(w); | ||||
|  | ||||
| 	w->layout = layout; | ||||
| 	return (layout); | ||||
| } | ||||
|  | ||||
| u_int | ||||
| layout_set_next(struct window *w) | ||||
| { | ||||
| 	u_int	layout = w->layout; | ||||
|  | ||||
| 	if (layout_sets[layout].arrange != NULL) | ||||
| 		layout_sets[layout].arrange(w); | ||||
|  | ||||
| 	w->layout++; | ||||
| 	if (w->layout > nitems(layout_sets) - 1) | ||||
| 		w->layout = 0; | ||||
| 	return (layout); | ||||
| } | ||||
|  | ||||
| u_int | ||||
| layout_set_previous(struct window *w) | ||||
| { | ||||
| 	u_int	layout = w->layout; | ||||
|  | ||||
| 	if (layout_sets[layout].arrange != NULL) | ||||
| 		layout_sets[layout].arrange(w); | ||||
|  | ||||
| 	if (w->layout == 0) | ||||
| 		w->layout = nitems(layout_sets) - 1; | ||||
| 	else | ||||
| 		w->layout--; | ||||
| 	return (layout); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_set_even_h(struct window *w) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	struct layout_cell	*lc, *lcnew; | ||||
| 	u_int			 i, n, width, xoff; | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n <= 1) | ||||
| 		return; | ||||
|  | ||||
| 	/* How many can we fit? */ | ||||
| 	if (w->sx / n < PANE_MINIMUM + 1) { | ||||
| 		width = PANE_MINIMUM + 1; | ||||
| 		n = UINT_MAX; | ||||
| 	} else | ||||
| 		width = w->sx / n; | ||||
|  | ||||
| 	/* Free the old root and construct a new. */ | ||||
| 	layout_free(w); | ||||
| 	lc = w->layout_root = layout_create_cell(NULL); | ||||
| 	layout_set_size(lc, w->sx, w->sy, 0, 0); | ||||
| 	layout_make_node(lc, LAYOUT_LEFTRIGHT); | ||||
|  | ||||
| 	/* Build new leaf cells. */ | ||||
| 	i = xoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		/* Create child cell. */ | ||||
| 		lcnew = layout_create_cell(lc); | ||||
| 		layout_set_size(lcnew, width - 1, w->sy, xoff, 0); | ||||
| 		layout_make_leaf(lcnew, wp); | ||||
| 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); | ||||
|  | ||||
| 		i++; | ||||
| 		xoff += width; | ||||
| 	} | ||||
|  | ||||
| 	/* Allocate any remaining space. */ | ||||
| 	if (w->sx > xoff - 1) { | ||||
| 		lc = TAILQ_LAST(&lc->cells, layout_cells); | ||||
| 		layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1)); | ||||
| 	} | ||||
|  | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(lc); | ||||
| 	layout_fix_panes(w, w->sx, w->sy); | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	server_redraw_window(w); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_set_even_v(struct window *w) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	struct layout_cell	*lc, *lcnew; | ||||
| 	u_int			 i, n, height, yoff; | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n <= 1) | ||||
| 		return; | ||||
|  | ||||
| 	/* How many can we fit? */ | ||||
| 	if (w->sy / n < PANE_MINIMUM + 1) { | ||||
| 		height = PANE_MINIMUM + 1; | ||||
| 		n = UINT_MAX; | ||||
| 	} else | ||||
| 		height = w->sy / n; | ||||
|  | ||||
| 	/* Free the old root and construct a new. */ | ||||
| 	layout_free(w); | ||||
| 	lc = w->layout_root = layout_create_cell(NULL); | ||||
| 	layout_set_size(lc, w->sx, w->sy, 0, 0); | ||||
| 	layout_make_node(lc, LAYOUT_TOPBOTTOM); | ||||
|  | ||||
| 	/* Build new leaf cells. */ | ||||
| 	i = yoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		/* Create child cell. */ | ||||
| 		lcnew = layout_create_cell(lc); | ||||
| 		layout_set_size(lcnew, w->sx, height - 1, 0, yoff); | ||||
| 		layout_make_leaf(lcnew, wp); | ||||
| 		TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); | ||||
|  | ||||
| 		i++; | ||||
| 		yoff += height; | ||||
| 	} | ||||
|  | ||||
| 	/* Allocate any remaining space. */ | ||||
| 	if (w->sy > yoff - 1) { | ||||
| 		lc = TAILQ_LAST(&lc->cells, layout_cells); | ||||
| 		layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1)); | ||||
| 	} | ||||
|  | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(lc); | ||||
| 	layout_fix_panes(w, w->sx, w->sy); | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	server_redraw_window(w); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_set_main_h(struct window *w) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	struct layout_cell	*lc, *lcmain, *lcrow, *lcchild; | ||||
| 	u_int			 n, mainheight, width, height, used; | ||||
| 	u_int			 i, j, columns, rows, totalrows; | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n <= 1) | ||||
| 		return; | ||||
| 	n--;	/* take off main pane */ | ||||
|  | ||||
| 	/* How many rows and columns will be needed? */ | ||||
| 	columns = w->sx / (PANE_MINIMUM + 1);	/* maximum columns */ | ||||
| 	rows = 1 + (n - 1) / columns; | ||||
| 	columns = 1 + (n - 1) / rows; | ||||
| 	width = w->sx / columns; | ||||
|  | ||||
| 	/* Get the main pane height and add one for separator line. */ | ||||
| 	mainheight = options_get_number(&w->options, "main-pane-height") + 1; | ||||
| 	if (mainheight < PANE_MINIMUM + 1) | ||||
| 		mainheight = PANE_MINIMUM + 1; | ||||
|  | ||||
| 	/* Try and make everything fit. */ | ||||
| 	totalrows = rows * (PANE_MINIMUM + 1) - 1; | ||||
| 	if (mainheight + totalrows > w->sy) { | ||||
| 		if (totalrows + PANE_MINIMUM + 1 > w->sy) | ||||
| 			mainheight = PANE_MINIMUM + 2; | ||||
| 		else | ||||
| 			mainheight = w->sy - totalrows; | ||||
| 		height = PANE_MINIMUM + 1; | ||||
| 	} else | ||||
| 		height = (w->sy - mainheight) / rows; | ||||
|  | ||||
| 	/* Free old tree and create a new root. */ | ||||
| 	layout_free(w); | ||||
| 	lc = w->layout_root = layout_create_cell(NULL); | ||||
| 	layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0); | ||||
| 	layout_make_node(lc, LAYOUT_TOPBOTTOM); | ||||
|  | ||||
| 	/* Create the main pane. */ | ||||
| 	lcmain = layout_create_cell(lc); | ||||
| 	layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); | ||||
| 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); | ||||
| 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); | ||||
|  | ||||
| 	/* Create a grid of the remaining cells. */ | ||||
| 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); | ||||
| 	for (j = 0; j < rows; j++) { | ||||
| 		/* If this is the last cell, all done. */ | ||||
| 		if (wp == NULL) | ||||
| 			break; | ||||
|  | ||||
| 		/* Create the new row. */ | ||||
| 		lcrow = layout_create_cell(lc); | ||||
| 		layout_set_size(lcrow, w->sx, height - 1, 0, 0); | ||||
| 		TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); | ||||
|  | ||||
| 		/* If only one column, just use the row directly. */ | ||||
| 		if (columns == 1) { | ||||
| 			layout_make_leaf(lcrow, wp); | ||||
| 			wp = TAILQ_NEXT(wp, entry); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
|  		/* Add in the columns. */ | ||||
| 		layout_make_node(lcrow, LAYOUT_LEFTRIGHT); | ||||
| 		for (i = 0; i < columns; i++) { | ||||
| 			/* Create and add a pane cell. */ | ||||
| 			lcchild = layout_create_cell(lcrow); | ||||
| 			layout_set_size(lcchild, width - 1, height - 1, 0, 0); | ||||
| 			layout_make_leaf(lcchild, wp); | ||||
| 			TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); | ||||
|  | ||||
| 			/* Move to the next cell. */ | ||||
| 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL) | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		/* Adjust the row to fit the full width if necessary. */ | ||||
| 		if (i == columns) | ||||
| 			i--; | ||||
|  		used = ((i + 1) * width) - 1; | ||||
|  		if (w->sx <= used) | ||||
|  			continue; | ||||
|  		lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); | ||||
|  		layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); | ||||
| 	} | ||||
|  | ||||
| 	/* Adjust the last row height to fit if necessary. */ | ||||
| 	used = mainheight + (rows * height) - 1; | ||||
| 	if (w->sy > used) { | ||||
| 		lcrow = TAILQ_LAST(&lc->cells, layout_cells); | ||||
| 		layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); | ||||
| 	} | ||||
|  | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(lc); | ||||
| 	layout_fix_panes(w, w->sx, w->sy); | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	server_redraw_window(w); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_set_main_v(struct window *w) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	struct layout_cell	*lc, *lcmain, *lccolumn, *lcchild; | ||||
| 	u_int			 n, mainwidth, width, height, used; | ||||
| 	u_int			 i, j, columns, rows, totalcolumns; | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n <= 1) | ||||
| 		return; | ||||
| 	n--;	/* take off main pane */ | ||||
|  | ||||
| 	/* How many rows and columns will be needed? */ | ||||
| 	rows = w->sy / (PANE_MINIMUM + 1);	/* maximum rows */ | ||||
| 	columns = 1 + (n - 1) / rows; | ||||
| 	rows = 1 + (n - 1) / columns; | ||||
| 	height = w->sy / rows; | ||||
|  | ||||
| 	/* Get the main pane width and add one for separator line. */ | ||||
| 	mainwidth = options_get_number(&w->options, "main-pane-width") + 1; | ||||
| 	if (mainwidth < PANE_MINIMUM + 1) | ||||
| 		mainwidth = PANE_MINIMUM + 1; | ||||
|  | ||||
| 	/* Try and make everything fit. */ | ||||
| 	totalcolumns = columns * (PANE_MINIMUM + 1) - 1; | ||||
| 	if (mainwidth + totalcolumns > w->sx) { | ||||
| 		if (totalcolumns + PANE_MINIMUM + 1 > w->sx) | ||||
| 			mainwidth = PANE_MINIMUM + 2; | ||||
| 		else | ||||
| 			mainwidth = w->sx - totalcolumns; | ||||
| 		width = PANE_MINIMUM + 1; | ||||
| 	} else | ||||
| 		width = (w->sx - mainwidth) / columns; | ||||
|  | ||||
| 	/* Free old tree and create a new root. */ | ||||
| 	layout_free(w); | ||||
| 	lc = w->layout_root = layout_create_cell(NULL); | ||||
| 	layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0); | ||||
| 	layout_make_node(lc, LAYOUT_LEFTRIGHT); | ||||
|  | ||||
| 	/* Create the main pane. */ | ||||
| 	lcmain = layout_create_cell(lc); | ||||
| 	layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); | ||||
| 	layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); | ||||
| 	TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); | ||||
|  | ||||
| 	/* Create a grid of the remaining cells. */ | ||||
| 	wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); | ||||
| 	for (j = 0; j < columns; j++) { | ||||
| 		/* If this is the last cell, all done. */ | ||||
| 		if (wp == NULL) | ||||
| 			break; | ||||
|  | ||||
| 		/* Create the new column. */ | ||||
| 		lccolumn = layout_create_cell(lc); | ||||
| 		layout_set_size(lccolumn, width - 1, w->sy, 0, 0); | ||||
| 		TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); | ||||
|  | ||||
| 		/* If only one row, just use the row directly. */ | ||||
| 		if (rows == 1) { | ||||
| 			layout_make_leaf(lccolumn, wp); | ||||
| 			wp = TAILQ_NEXT(wp, entry); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Add in the rows. */ | ||||
| 		layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); | ||||
| 		for (i = 0; i < rows; i++) { | ||||
| 			/* Create and add a pane cell. */ | ||||
| 			lcchild = layout_create_cell(lccolumn); | ||||
| 			layout_set_size(lcchild, width - 1, height - 1, 0, 0); | ||||
| 			layout_make_leaf(lcchild, wp); | ||||
| 			TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); | ||||
|  | ||||
| 			/* Move to the next cell. */ | ||||
| 			if ((wp = TAILQ_NEXT(wp, entry)) == NULL) | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		/* Adjust the column to fit the full height if necessary. */ | ||||
| 		if (i == rows) | ||||
| 			i--; | ||||
| 		used = ((i + 1) * height) - 1; | ||||
|  		if (w->sy <= used) | ||||
|  			continue; | ||||
|  		lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); | ||||
|  		layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used); | ||||
| 	} | ||||
|  | ||||
| 	/* Adjust the last column width to fit if necessary. */ | ||||
| 	used = mainwidth + (columns * width) - 1; | ||||
| 	if (w->sx > used) { | ||||
| 		lccolumn = TAILQ_LAST(&lc->cells, layout_cells); | ||||
| 		layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used); | ||||
| 	} | ||||
|  | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(lc); | ||||
| 	layout_fix_panes(w, w->sx, w->sy); | ||||
|  | ||||
| 	layout_print_cell(w->layout_root, __func__, 1); | ||||
|  | ||||
| 	server_redraw_window(w); | ||||
| } | ||||
							
								
								
									
										869
									
								
								layout.c
									
									
									
									
									
								
							
							
						
						
									
										869
									
								
								layout.c
									
									
									
									
									
								
							| @@ -18,349 +18,632 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| /* | ||||
|  * Each layout has two functions, _refresh to relayout the panes and _resize to | ||||
|  * resize a single pane. | ||||
|  * The window layout is a tree of cells each of which can be one of: a | ||||
|  * left-right container for a list of cells, a top-bottom container for a list | ||||
|  * of cells, or a container for a window pane. | ||||
|  * | ||||
|  * Second argument (int) to _refresh is 1 if the only change has been that the | ||||
|  * active pane has changed. If 0 then panes, active pane or both may have | ||||
|  * changed. | ||||
|  * Each window has a pointer to the root of its layout tree (containing its | ||||
|  * panes), every pane has a pointer back to the cell containing it, and each | ||||
|  * cell a pointer to its parent cell. | ||||
|  */ | ||||
|  | ||||
| void	layout_active_only_refresh(struct window *, int); | ||||
| void	layout_even_h_refresh(struct window *, int); | ||||
| void	layout_even_v_refresh(struct window *, int); | ||||
| void	layout_main_h_refresh(struct window *, int); | ||||
| void	layout_main_v_refresh(struct window *, int); | ||||
| int	layout_resize_pane_grow(struct layout_cell *, enum layout_type, int); | ||||
| int	layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int); | ||||
|  | ||||
| const struct { | ||||
| 	const char     *name; | ||||
| 	void		(*refresh)(struct window *, int); | ||||
| 	void		(*resize)(struct window_pane *, int); | ||||
| } layouts[] = { | ||||
| 	{ "manual-vertical", layout_manual_v_refresh, layout_manual_v_resize }, | ||||
| 	{ "active-only", layout_active_only_refresh, NULL }, | ||||
| 	{ "even-horizontal", layout_even_h_refresh, NULL }, | ||||
| 	{ "even-vertical", layout_even_v_refresh, NULL }, | ||||
| 	{ "main-horizontal", layout_main_h_refresh, NULL }, | ||||
| 	{ "main-vertical", layout_main_v_refresh, NULL }, | ||||
| }; | ||||
|  | ||||
| const char * | ||||
| layout_name(struct window *w) | ||||
| struct layout_cell * | ||||
| layout_create_cell(struct layout_cell *lcparent) | ||||
| { | ||||
| 	return (layouts[w->layout].name); | ||||
| 	struct layout_cell	*lc; | ||||
|  | ||||
| 	lc = xmalloc(sizeof *lc); | ||||
| 	lc->type = LAYOUT_WINDOWPANE; | ||||
| 	lc->parent = lcparent; | ||||
|  | ||||
| 	TAILQ_INIT(&lc->cells); | ||||
| 	 | ||||
| 	lc->sx = UINT_MAX; | ||||
| 	lc->sy = UINT_MAX; | ||||
| 	 | ||||
| 	lc->xoff = UINT_MAX; | ||||
| 	lc->yoff = UINT_MAX; | ||||
| 	 | ||||
| 	lc->wp = NULL; | ||||
|  | ||||
| 	return (lc); | ||||
| } | ||||
|  | ||||
| int | ||||
| layout_lookup(const char *name) | ||||
| void | ||||
| layout_free_cell(struct layout_cell *lc) | ||||
| { | ||||
| 	u_int	i; | ||||
| 	int	matched = -1; | ||||
| 	struct layout_cell	*lcchild; | ||||
|  | ||||
| 	for (i = 0; i < nitems(layouts); i++) { | ||||
| 		if (strncmp(layouts[i].name, name, strlen(name)) == 0) { | ||||
| 			if (matched != -1)	/* ambiguous */ | ||||
| 				return (-1); | ||||
| 			matched = i; | ||||
| 	switch (lc->type) { | ||||
| 	case LAYOUT_LEFTRIGHT: | ||||
| 	case LAYOUT_TOPBOTTOM: | ||||
| 		while (!TAILQ_EMPTY(&lc->cells)) { | ||||
| 			lcchild = TAILQ_FIRST(&lc->cells); | ||||
| 			TAILQ_REMOVE(&lc->cells, lcchild, entry); | ||||
| 			layout_free_cell(lcchild); | ||||
| 		} | ||||
| 		break; | ||||
| 	case LAYOUT_WINDOWPANE: | ||||
| 		if (lc->wp != NULL) | ||||
| 			lc->wp->layout_cell = NULL; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return (matched); | ||||
| } | ||||
|  | ||||
| int | ||||
| layout_select(struct window *w, u_int layout) | ||||
| { | ||||
| 	if (layout > nitems(layouts) - 1 || layout == w->layout) | ||||
| 		return (-1); | ||||
| 	w->layout = layout; | ||||
|  | ||||
| 	layout_refresh(w, 0); | ||||
| 	return (0); | ||||
| 	xfree(lc); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_next(struct window *w) | ||||
| layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) | ||||
| { | ||||
| 	w->layout++; | ||||
| 	if (w->layout > nitems(layouts) - 1) | ||||
| 		w->layout = 0; | ||||
| 	layout_refresh(w, 0); | ||||
| 	struct layout_cell	*lcchild; | ||||
|  | ||||
| 	log_debug( | ||||
| 	    "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, | ||||
| 	    lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); | ||||
| 	switch (lc->type) { | ||||
| 	case LAYOUT_LEFTRIGHT: | ||||
| 	case LAYOUT_TOPBOTTOM: | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) | ||||
| 		    	layout_print_cell(lcchild, hdr, n + 1); | ||||
| 		break; | ||||
| 	case LAYOUT_WINDOWPANE: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_previous(struct window *w) | ||||
| layout_set_size( | ||||
|     struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) | ||||
| { | ||||
| 	if (w->layout == 0) | ||||
| 		w->layout = nitems(layouts) - 1; | ||||
| 	else | ||||
| 		w->layout--; | ||||
| 	layout_refresh(w, 0); | ||||
| 	lc->sx = sx; | ||||
| 	lc->sy = sy; | ||||
|  | ||||
| 	lc->xoff = xoff; | ||||
| 	lc->yoff = yoff; | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_refresh(struct window *w, int active_only) | ||||
| layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) | ||||
| { | ||||
| 	layouts[w->layout].refresh(w, active_only); | ||||
| 	server_redraw_window(w); | ||||
| } | ||||
| 	lc->type = LAYOUT_WINDOWPANE; | ||||
|  | ||||
| int | ||||
| layout_resize(struct window_pane *wp, int adjust) | ||||
| { | ||||
| 	struct window	*w = wp->window; | ||||
| 	TAILQ_INIT(&lc->cells); | ||||
|  | ||||
| 	if (layouts[w->layout].resize == NULL) | ||||
| 		return (-1); | ||||
| 	layouts[w->layout].resize(wp, adjust); | ||||
| 	return (0); | ||||
| 	wp->layout_cell = lc; | ||||
| 	lc->wp = wp; | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_active_only_refresh(struct window *w, unused int active_only) | ||||
| layout_make_node(struct layout_cell *lc, enum layout_type type) | ||||
| { | ||||
| 	if (type == LAYOUT_WINDOWPANE) | ||||
| 		fatalx("bad layout type"); | ||||
| 	lc->type = type; | ||||
|  | ||||
| 	TAILQ_INIT(&lc->cells); | ||||
|  | ||||
| 	if (lc->wp != NULL) | ||||
| 		lc->wp->layout_cell = NULL; | ||||
| 	lc->wp = NULL; | ||||
| } | ||||
|  | ||||
| /* Fix cell offsets based on their sizes. */ | ||||
| void | ||||
| layout_fix_offsets(struct layout_cell *lc) | ||||
| { | ||||
| 	struct layout_cell	*lcchild; | ||||
| 	u_int			 xoff, yoff; | ||||
|  | ||||
| 	if (lc->type == LAYOUT_LEFTRIGHT) { | ||||
| 		xoff = lc->xoff; | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) { | ||||
| 			lcchild->xoff = xoff; | ||||
| 			lcchild->yoff = lc->yoff; | ||||
| 			if (lcchild->type != LAYOUT_WINDOWPANE) | ||||
| 				layout_fix_offsets(lcchild); | ||||
| 			xoff += lcchild->sx + 1; | ||||
| 		} | ||||
| 	} else { | ||||
| 		yoff = lc->yoff; | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) { | ||||
| 			lcchild->xoff = lc->xoff; | ||||
| 			lcchild->yoff = yoff; | ||||
| 			if (lcchild->type != LAYOUT_WINDOWPANE) | ||||
| 				layout_fix_offsets(lcchild); | ||||
| 			yoff += lcchild->sy + 1; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Update pane offsets and sizes based on their cells. */ | ||||
| void | ||||
| layout_fix_panes(struct window *w, u_int wsx, u_int wsy) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 xoff; | ||||
| 	struct layout_cell	*lc; | ||||
| 	u_int			 sx, sy; | ||||
|  | ||||
| 	xoff = w->sx; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		/* Put the active pane on screen and the rest to the right. */ | ||||
| 		if (wp == w->active) | ||||
| 			wp->xoff = 0; | ||||
| 		if ((lc = wp->layout_cell) == NULL) | ||||
| 			continue; | ||||
| 		wp->xoff = lc->xoff; | ||||
| 		wp->yoff = lc->yoff; | ||||
|  | ||||
| 		/* | ||||
| 		 * Layout cells are limited by the smallest size of other cells | ||||
| 		 * within the same row or column; if this isn't the case | ||||
| 		 * resizing becomes difficult. | ||||
| 		 * | ||||
| 		 * However, panes do not have to take up their entire cell, so | ||||
| 		 * they can be cropped to the window edge if the layout | ||||
| 		 * overflows and they are partly visible. | ||||
| 		 * | ||||
| 		 * This stops cells being hidden unnecessarily. | ||||
| 		 */ | ||||
|  | ||||
| 		/* | ||||
| 		 * Work out the horizontal size. If the pane is actually | ||||
| 		 * outside the window or the entire pane is already visible, | ||||
| 		 * don't crop. | ||||
| 		 */ | ||||
| 		if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx) | ||||
| 			sx = lc->sx; | ||||
| 		else { | ||||
| 			wp->xoff = xoff; | ||||
| 			xoff += w->sx; | ||||
| 			sx = wsx - lc->xoff; | ||||
| 			if (sx < 1) | ||||
| 				sx = lc->sx; | ||||
| 		} | ||||
| 		wp->yoff = 0; | ||||
| 		window_pane_resize(wp, w->sx, w->sy); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_even_h_refresh(struct window *w, int active_only) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 i, n, width, xoff; | ||||
|  | ||||
| 	if (active_only) | ||||
| 		return; | ||||
|  | ||||
| 	/* If the screen is too small, show active only. */ | ||||
| 	if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) { | ||||
| 		layout_active_only_refresh(w, active_only); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n == 0) | ||||
| 		return; | ||||
|  | ||||
| 	/* How many can we fit? */ | ||||
| 	if (w->sx / n < PANE_MINIMUM) { | ||||
| 		width = PANE_MINIMUM; | ||||
| 		n = UINT_MAX; | ||||
| 	} else | ||||
| 		width = w->sx / n; | ||||
|  | ||||
| 	/* Fit the panes. */ | ||||
| 	i = xoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		wp->xoff = xoff; | ||||
| 		wp->yoff = 0; | ||||
| 		if (i != n - 1) | ||||
|  			window_pane_resize(wp, width - 1, w->sy); | ||||
| 		else | ||||
|  			window_pane_resize(wp, width, w->sy); | ||||
|  | ||||
| 		i++; | ||||
| 		xoff += width; | ||||
| 	} | ||||
|  | ||||
| 	/* Any space left? */ | ||||
| 	while (xoff++ < w->sx) { | ||||
| 		wp = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		window_pane_resize(wp, wp->sx + 1, wp->sy); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_even_v_refresh(struct window *w, int active_only) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 i, n, height, yoff; | ||||
|  | ||||
| 	if (active_only) | ||||
| 		return; | ||||
|  | ||||
| 	/* If the screen is too small, show active only. */ | ||||
| 	if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) { | ||||
| 		layout_active_only_refresh(w, active_only); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n == 0) | ||||
| 		return; | ||||
|  | ||||
| 	/* How many can we fit? */ | ||||
| 	if (w->sy / n < PANE_MINIMUM) { | ||||
| 		height = PANE_MINIMUM; | ||||
| 		n = UINT_MAX; | ||||
| 	} else | ||||
| 		height = w->sy / n; | ||||
|  | ||||
| 	/* Fit the panes. */ | ||||
| 	i = yoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		wp->xoff = 0; | ||||
| 		wp->yoff = yoff; | ||||
| 		if (i != n - 1) | ||||
|  			window_pane_resize(wp, w->sx, height - 1); | ||||
| 		else | ||||
|  			window_pane_resize(wp, w->sx, height); | ||||
|  | ||||
| 		i++; | ||||
| 		yoff += height; | ||||
| 	} | ||||
|  | ||||
| 	/* Any space left? */ | ||||
| 	while (yoff++ < w->sy) { | ||||
| 		wp = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		window_pane_resize(wp, wp->sx, wp->sy + 1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_main_v_refresh(struct window *w, int active_only) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 i, n, mainwidth, height, yoff; | ||||
|  | ||||
| 	if (active_only) | ||||
| 		return; | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n == 0) | ||||
| 		return; | ||||
|  | ||||
| 	/* Get the main pane width and add one for separator line. */ | ||||
| 	mainwidth = options_get_number(&w->options, "main-pane-width") + 1; | ||||
|  | ||||
| 	/* Need >1 pane and minimum columns; if fewer, display active only. */ | ||||
| 	if (n == 1 || | ||||
| 	    w->sx < mainwidth + PANE_MINIMUM || w->sy < PANE_MINIMUM) { | ||||
| 		layout_active_only_refresh(w, active_only); | ||||
| 		return; | ||||
| 	} | ||||
| 	n--; | ||||
|  | ||||
| 	/* How many can we fit, not including first? */ | ||||
| 	if (w->sy / n < PANE_MINIMUM) { | ||||
| 		height = PANE_MINIMUM; | ||||
| 		n = w->sy / PANE_MINIMUM; | ||||
| 	} else | ||||
| 		height = w->sy / n; | ||||
|  | ||||
| 	/* Fit the panes. */ | ||||
| 	i = yoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		if (wp == TAILQ_FIRST(&w->panes)) { | ||||
| 			wp->xoff = 0; | ||||
| 			wp->yoff = 0; | ||||
| 			window_pane_resize(wp, mainwidth - 1, w->sy); | ||||
| 			continue; | ||||
| 		 | ||||
| 		/*  | ||||
| 		 * Similarly for the vertical size; the minimum vertical size | ||||
| 		 * is two because scroll regions cannot be one line. | ||||
| 		 */ | ||||
| 		if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy) | ||||
| 			sy = lc->sy; | ||||
| 		else { | ||||
| 			sy = wsy - lc->yoff; | ||||
| 			if (sy < 2) | ||||
| 				sy = lc->sy; | ||||
| 		} | ||||
|  | ||||
| 		wp->xoff = mainwidth; | ||||
| 		wp->yoff = yoff; | ||||
| 		if (i != n - 1) | ||||
|  			window_pane_resize(wp, w->sx - mainwidth, height - 1); | ||||
| 		else | ||||
|  			window_pane_resize(wp, w->sx - mainwidth, height); | ||||
| 		window_pane_resize(wp, sx, sy); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 		i++; | ||||
| 		yoff += height; | ||||
| /* Calculate how much size is available to be removed from a cell. */ | ||||
| u_int | ||||
| layout_resize_check(struct layout_cell *lc, enum layout_type type) | ||||
| { | ||||
| 	struct layout_cell	*lcchild; | ||||
| 	u_int			 available, minimum; | ||||
|  | ||||
| 	if (lc->type == LAYOUT_WINDOWPANE) { | ||||
| 		/* Space available in this cell only. */ | ||||
| 		if (type == LAYOUT_LEFTRIGHT) | ||||
| 			available = lc->sx; | ||||
| 		else | ||||
| 			available = lc->sy; | ||||
| 		 | ||||
| 		if (available > PANE_MINIMUM) | ||||
| 			available -= PANE_MINIMUM; | ||||
| 		else | ||||
| 			available = 0; | ||||
| 	} else if (lc->type == type) { | ||||
| 		/* Same type: total of available space in all child cells. */ | ||||
| 		available = 0; | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) | ||||
| 			available += layout_resize_check(lcchild, type); | ||||
| 	} else { | ||||
| 		/* Different type: minimum of available space in child cells. */ | ||||
| 		minimum = UINT_MAX; | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) { | ||||
| 			available = layout_resize_check(lcchild, type); | ||||
| 			if (available < minimum) | ||||
| 				minimum = available; | ||||
| 		} | ||||
| 		available = minimum; | ||||
| 	} | ||||
|  | ||||
| 	/* Any space left? */ | ||||
| 	while (yoff++ < w->sy) { | ||||
| 		wp = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		while (wp != NULL && wp == TAILQ_FIRST(&w->panes)) | ||||
| 			wp = TAILQ_PREV(wp, window_panes, entry); | ||||
| 		if (wp == NULL) | ||||
| 	return (available); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Adjust cell size evenly, including altering its children. This function | ||||
|  * expects the change to have already been bounded to the space available. | ||||
|  */ | ||||
| void | ||||
| layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change) | ||||
| { | ||||
| 	struct layout_cell	*lcchild; | ||||
|  | ||||
| 	/* Adjust the cell size. */ | ||||
| 	if (type == LAYOUT_LEFTRIGHT) | ||||
| 		lc->sx += change; | ||||
| 	else | ||||
| 		lc->sy += change; | ||||
| 	 | ||||
| 	/* If this is a leaf cell, that is all that is necessary. */ | ||||
| 	if (type == LAYOUT_WINDOWPANE) | ||||
| 		return; | ||||
|  | ||||
| 	/* Child cell runs in a different direction. */ | ||||
| 	if (lc->type != type) { | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) | ||||
| 			layout_resize_adjust(lcchild, type, change); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/*  | ||||
| 	 * Child cell runs in the same direction. Adjust each child equally  | ||||
| 	 * until no further change is possible. | ||||
| 	 */ | ||||
| 	while (change != 0) { | ||||
| 		TAILQ_FOREACH(lcchild, &lc->cells, entry) { | ||||
| 			if (change == 0) | ||||
| 				break; | ||||
| 			if (change > 0) { | ||||
| 				layout_resize_adjust(lcchild, type, 1); | ||||
| 				change--; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (layout_resize_check(lcchild, type) > 0) { | ||||
| 				layout_resize_adjust(lcchild, type, -1); | ||||
| 				change++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_init(struct window *w) | ||||
| { | ||||
| 	struct layout_cell	*lc; | ||||
|  | ||||
| 	lc = w->layout_root = layout_create_cell(NULL); | ||||
| 	layout_set_size(lc, w->sx, w->sy, 0, 0); | ||||
| 	layout_make_leaf(lc, TAILQ_FIRST(&w->panes)); | ||||
|  | ||||
| 	layout_fix_panes(w, w->sx, w->sy); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_free(struct window *w) | ||||
| { | ||||
| 	layout_free_cell(w->layout_root); | ||||
| } | ||||
|  | ||||
| /* Resize the entire layout after window resize. */ | ||||
| void | ||||
| layout_resize(struct window *w, u_int sx, u_int sy) | ||||
| { | ||||
| 	struct layout_cell	*lc = w->layout_root; | ||||
| 	int			 xlimit, ylimit, xchange, ychange; | ||||
|  | ||||
| 	/*  | ||||
| 	 * Adjust horizontally. Do not attempt to reduce the layout lower than | ||||
| 	 * the minimum (more than the amount returned by layout_resize_check). | ||||
| 	 *  | ||||
| 	 * This can mean that the window size is smaller than the total layout | ||||
| 	 * size: redrawing this is handled at a higher level, but it does leave | ||||
| 	 * a problem with growing the window size here: if the current size is | ||||
| 	 * < the minimum, growing proportionately by adding to each pane is | ||||
| 	 * wrong as it would keep the layout size larger than the window size. | ||||
| 	 * Instead, spread the difference between the minimum and the new size | ||||
| 	 * out proportionately - this should leave the layout fitting the new | ||||
| 	 * window size. | ||||
| 	 */ | ||||
| 	xchange = sx - w->sx; | ||||
| 	xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT); | ||||
| 	if (xchange < 0 && xchange < -xlimit) | ||||
| 		xchange = -xlimit; | ||||
| 	if (xlimit == 0) { | ||||
| 		if (sx <= lc->sx)	/* lc->sx is minimum possible */ | ||||
| 			xchange = 0; | ||||
| 		else | ||||
| 			xchange = sx - lc->sx; | ||||
| 	} | ||||
| 	if (xchange != 0) | ||||
| 		layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange); | ||||
|  | ||||
| 	/* Adjust vertically in a similar fashion. */ | ||||
| 	ychange = sy - w->sy; | ||||
| 	ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM); | ||||
| 	if (ychange < 0 && ychange < -ylimit) | ||||
| 		ychange = -ylimit; | ||||
| 	if (ylimit == 0) { | ||||
| 		if (sy <= lc->sy)	/* lc->sy is minimum possible */ | ||||
| 			ychange = 0; | ||||
| 		else | ||||
| 			ychange = sy - lc->sy; | ||||
| 	} | ||||
| 	if (ychange != 0) | ||||
| 		layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange); | ||||
| 	 | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(lc); | ||||
| 	layout_fix_panes(w, sx, sy); | ||||
| } | ||||
|  | ||||
| /* Resize a single pane within the layout. */ | ||||
| void | ||||
| layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) | ||||
| { | ||||
| 	struct layout_cell     *lc, *lcparent; | ||||
| 	int			needed, size; | ||||
|  | ||||
| 	lc = wp->layout_cell; | ||||
|  | ||||
| 	/* Find next parent of the same type. */ | ||||
| 	lcparent = lc->parent; | ||||
| 	while (lcparent != NULL && lcparent->type != type) { | ||||
| 		lc = lcparent; | ||||
| 		lcparent = lc->parent; | ||||
| 	} | ||||
| 	if (lcparent == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	/* If this is the last cell, move back one. */ | ||||
| 	if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) | ||||
| 		lc = TAILQ_PREV(lc, layout_cells, entry); | ||||
|  | ||||
| 	/* Grow or shrink the cell. */ | ||||
| 	needed = change; | ||||
| 	while (needed != 0) { | ||||
| 		if (change > 0) { | ||||
| 			size = layout_resize_pane_grow(lc, type, needed); | ||||
| 			needed -= size; | ||||
| 		} else { | ||||
| 			size = layout_resize_pane_shrink(lc, type, needed); | ||||
| 			needed += size; | ||||
| 		} | ||||
|  | ||||
| 		if (size == 0)	/* no more change possible */ | ||||
| 			break; | ||||
| 		window_pane_resize(wp, wp->sx, wp->sy + 1); | ||||
| 	} | ||||
| 	 | ||||
| 	/* Fix cell offsets. */ | ||||
| 	layout_fix_offsets(wp->window->layout_root); | ||||
| 	layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); | ||||
| } | ||||
|  | ||||
| void | ||||
| layout_main_h_refresh(struct window *w, int active_only) | ||||
| int | ||||
| layout_resize_pane_grow( | ||||
|     struct layout_cell *lc, enum layout_type type, int needed) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 i, n, mainheight, width, xoff; | ||||
| 	struct layout_cell	*lcadd, *lcremove; | ||||
| 	u_int			 size; | ||||
|  | ||||
| 	if (active_only) | ||||
| 		return; | ||||
|  | ||||
| 	/* Get number of panes. */ | ||||
| 	n = window_count_panes(w); | ||||
| 	if (n == 0) | ||||
| 		return; | ||||
|  | ||||
| 	/* Get the main pane height and add one for separator line. */ | ||||
| 	mainheight = options_get_number(&w->options, "main-pane-height") + 1; | ||||
|  | ||||
| 	/* Need >1 pane and minimum rows; if fewer, display active only. */ | ||||
| 	if (n == 1 || | ||||
| 	    w->sy < mainheight + PANE_MINIMUM || w->sx < PANE_MINIMUM) { | ||||
| 		layout_active_only_refresh(w, active_only); | ||||
| 		return; | ||||
| 	} | ||||
| 	n--; | ||||
|  | ||||
| 	/* How many can we fit, not including first? */ | ||||
| 	if (w->sx / n < PANE_MINIMUM) { | ||||
| 		width = PANE_MINIMUM; | ||||
| 		n = w->sx / PANE_MINIMUM; | ||||
| 	} else | ||||
| 		width = w->sx / n; | ||||
|  | ||||
| 	/* Fit the panes. */ | ||||
| 	i = xoff = 0; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		if (wp == TAILQ_FIRST(&w->panes)) { | ||||
| 			wp->xoff = 0; | ||||
| 			wp->yoff = 0; | ||||
| 			window_pane_resize(wp, w->sx, mainheight - 1); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		wp->xoff = xoff; | ||||
| 		wp->yoff = mainheight; | ||||
| 		if (i != n - 1) | ||||
|  			window_pane_resize(wp, width - 1, w->sy - mainheight); | ||||
| 		else | ||||
|  			window_pane_resize(wp, width - 1, w->sy - mainheight); | ||||
|  | ||||
| 		i++; | ||||
| 		xoff += width; | ||||
| 	} | ||||
|  | ||||
| 	/* Any space left? */ | ||||
| 	while (xoff++ < w->sx + 1) { | ||||
| 		wp = TAILQ_LAST(&w->panes, window_panes); | ||||
| 		while (wp != NULL && wp == TAILQ_FIRST(&w->panes)) | ||||
| 			wp = TAILQ_PREV(wp, window_panes, entry); | ||||
| 		if (wp == NULL) | ||||
| 	/* Growing. Always add to the current cell. */ | ||||
| 	lcadd = lc; | ||||
| 			 | ||||
| 	/* Look towards the tail for a suitable cell for reduction. */ | ||||
| 	lcremove = TAILQ_NEXT(lc, entry); | ||||
| 	while (lcremove != NULL) { | ||||
| 		size = layout_resize_check(lcremove, type); | ||||
| 		if (size > 0) | ||||
| 			break; | ||||
| 		window_pane_resize(wp, wp->sx + 1, wp->sy); | ||||
| 		lcremove = TAILQ_NEXT(lcremove, entry);	 | ||||
| 	} | ||||
|  | ||||
| 	/* If none found, look towards the head. */ | ||||
| 	if (lcremove == NULL) { | ||||
| 		lcremove = TAILQ_PREV(lc, layout_cells, entry); | ||||
| 		while (lcremove != NULL) { | ||||
| 			size = layout_resize_check(lcremove, type); | ||||
| 			if (size > 0) | ||||
| 				break; | ||||
| 			lcremove = TAILQ_PREV(lcremove, layout_cells, entry); | ||||
| 		} | ||||
| 		if (lcremove == NULL) | ||||
| 			return (0); | ||||
| 	} | ||||
|  | ||||
| 	/* Change the cells. */ | ||||
| 	if (size > (u_int) needed) | ||||
| 		size = needed; | ||||
| 	layout_resize_adjust(lcadd, type, size); | ||||
| 	layout_resize_adjust(lcremove, type, -size); | ||||
| 	return (size); | ||||
| } | ||||
|  | ||||
| int | ||||
| layout_resize_pane_shrink( | ||||
|     struct layout_cell *lc, enum layout_type type, int needed) | ||||
| { | ||||
| 	struct layout_cell	*lcadd, *lcremove; | ||||
| 	u_int			 size; | ||||
|  | ||||
| 	/* Shrinking. Find cell to remove from by walking towards head. */ | ||||
| 	lcremove = lc; | ||||
| 	do { | ||||
| 		size = layout_resize_check(lcremove, type); | ||||
| 		if (size != 0) | ||||
| 			break; | ||||
| 		lcremove = TAILQ_PREV(lcremove, layout_cells, entry); | ||||
| 	} while (lcremove != NULL); | ||||
| 	if (lcremove == NULL) | ||||
| 		return (0); | ||||
|  | ||||
| 	/* And add onto the next cell (from the original cell). */ | ||||
| 	lcadd = TAILQ_NEXT(lc, entry); | ||||
| 	if (lcadd == NULL) | ||||
| 		return (0); | ||||
|  | ||||
| 	/* Change the cells. */ | ||||
| 	if (size > (u_int) -needed) | ||||
| 		size = -needed; | ||||
| 	layout_resize_adjust(lcadd, type, size); | ||||
| 	layout_resize_adjust(lcremove, type, -size); | ||||
| 	return (size); | ||||
| } | ||||
|  | ||||
| /* Split a pane into two. size is a hint, or -1 for default half/half split. */ | ||||
| int | ||||
| layout_split_pane(struct window_pane *wp, | ||||
|     enum layout_type type, int size, struct window_pane *new_wp) | ||||
| { | ||||
| 	struct layout_cell     *lc, *lcparent, *lcnew; | ||||
| 	u_int			sx, sy, xoff, yoff, size1, size2; | ||||
|  | ||||
| 	lc = wp->layout_cell; | ||||
|  | ||||
| 	/* Copy the old cell size. */ | ||||
| 	sx = lc->sx; | ||||
| 	sy = lc->sy; | ||||
| 	xoff = lc->xoff; | ||||
| 	yoff = lc->yoff; | ||||
|  | ||||
| 	/* Check there is enough space for the two new panes. */ | ||||
| 	switch (type) { | ||||
| 	case LAYOUT_LEFTRIGHT: | ||||
| 		if (sx < PANE_MINIMUM * 2 + 1) | ||||
| 			return (-1); | ||||
| 		break; | ||||
| 	case LAYOUT_TOPBOTTOM: | ||||
| 		if (sy < PANE_MINIMUM * 2 + 1) | ||||
| 			return (-1); | ||||
| 		break; | ||||
| 	default: | ||||
| 		fatalx("bad layout type"); | ||||
| 	} | ||||
| 	 | ||||
| 	if (lc->parent != NULL && lc->parent->type == type) { | ||||
| 		/* | ||||
| 		 * If the parent exists and is of the same type as the split, | ||||
| 		 * create a new cell and insert it after this one. | ||||
| 		 */ | ||||
|  | ||||
| 		/* Create the new child cell. */ | ||||
| 		lcnew = layout_create_cell(lc->parent); | ||||
| 		TAILQ_INSERT_AFTER(&lc->parent->cells, lc, lcnew, entry); | ||||
| 	} else { | ||||
| 		/* | ||||
| 		 * Otherwise create a new parent and insert it. | ||||
| 		 */ | ||||
| 		 | ||||
| 		/* Create and insert the replacement parent. */ | ||||
| 		lcparent = layout_create_cell(lc->parent); | ||||
| 		layout_make_node(lcparent, type); | ||||
| 		layout_set_size(lcparent, sx, sy, xoff, yoff); | ||||
| 		if (lc->parent == NULL) | ||||
| 			wp->window->layout_root = lcparent; | ||||
| 		else | ||||
| 			TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); | ||||
| 		 | ||||
| 		/* Insert the old cell. */ | ||||
| 		lc->parent = lcparent; | ||||
| 		TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); | ||||
| 		 | ||||
| 		/* Create the new child cell. */ | ||||
| 		lcnew = layout_create_cell(lcparent); | ||||
| 		TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); | ||||
| 	} | ||||
|  | ||||
| 	/* Set new cell sizes.  size is the target size or -1 for middle split, | ||||
| 	 * size1 is the size of the top/left and size2 the bottom/right. | ||||
| 	 */ | ||||
| 	switch (type) { | ||||
| 	case LAYOUT_LEFTRIGHT: | ||||
| 		if (size < 0) | ||||
| 			size2 = ((sx + 1) / 2) - 1; | ||||
|  		else | ||||
| 			size2 = size; | ||||
| 		if (size2 < PANE_MINIMUM) | ||||
| 			size2 = PANE_MINIMUM; | ||||
| 		else if (size2 > sx - 2) | ||||
| 			size2 = sx - 2; | ||||
| 		size1 = sx - 1 - size2; | ||||
| 		layout_set_size(lc, size1, sy, xoff, yoff); | ||||
| 		layout_set_size(lcnew, size2, sy, xoff + lc->sx + 1, yoff); | ||||
| 		break; | ||||
| 	case LAYOUT_TOPBOTTOM: | ||||
| 		if (size < 0) | ||||
| 			size2 = ((sy + 1) / 2) - 1; | ||||
| 		else | ||||
| 			size2 = size; | ||||
| 		if (size2 < PANE_MINIMUM) | ||||
| 			size2 = PANE_MINIMUM; | ||||
| 		else if (size2 > sy - 2) | ||||
| 			size2 = sy - 2; | ||||
| 		size1 = sy - 1 - size2; | ||||
| 		layout_set_size(lc, sx, size1, xoff, yoff); | ||||
| 		layout_set_size(lcnew, sx, size2, xoff, yoff + lc->sy + 1); | ||||
| 		break; | ||||
| 	default: | ||||
| 		fatalx("bad layout type"); | ||||
| 	} | ||||
|  | ||||
| 	/* Assign the panes. */ | ||||
| 	layout_make_leaf(lc, wp); | ||||
| 	layout_make_leaf(lcnew, new_wp); | ||||
|  | ||||
| 	/* Fix pane offsets and sizes. */ | ||||
| 	layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); | ||||
|  | ||||
| 	return (0); | ||||
| } | ||||
|  | ||||
| /* Destroy the layout associated with a pane and redistribute the space. */ | ||||
| void | ||||
| layout_close_pane(struct window_pane *wp) | ||||
| { | ||||
| 	struct layout_cell     *lc, *lcother, *lcparent; | ||||
|  | ||||
| 	lc = wp->layout_cell; | ||||
| 	lcparent = lc->parent; | ||||
|  | ||||
| 	/*  | ||||
| 	 * If no parent, this is the last pane so window close is imminent and | ||||
| 	 * there is no need to resize anything. | ||||
| 	 */ | ||||
| 	if (lcparent == NULL) { | ||||
| 		layout_free_cell(lc); | ||||
| 		wp->window->layout_root = NULL; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Merge the space into the previous or next cell. */ | ||||
| 	if (lc == TAILQ_FIRST(&lcparent->cells)) | ||||
| 		lcother = TAILQ_NEXT(lc, entry); | ||||
| 	else | ||||
| 		lcother = TAILQ_PREV(lc, layout_cells, entry); | ||||
| 	if (lcparent->type == LAYOUT_LEFTRIGHT) | ||||
| 		layout_resize_adjust(lcother, lcparent->type, lc->sx + 1); | ||||
| 	else | ||||
| 		layout_resize_adjust(lcother, lcparent->type, lc->sy + 1); | ||||
|  | ||||
| 	/* Remove this from the parent's list. */ | ||||
| 	TAILQ_REMOVE(&lcparent->cells, lc, entry); | ||||
| 	layout_free_cell(lc); | ||||
| 	 | ||||
| 	/*  | ||||
| 	 * If the parent now has one cell, remove the parent from the tree and | ||||
| 	 * replace it by that cell. | ||||
| 	 */ | ||||
| 	lc = TAILQ_FIRST(&lcparent->cells); | ||||
| 	if (TAILQ_NEXT(lc, entry) == NULL) { | ||||
| 		TAILQ_REMOVE(&lcparent->cells, lc, entry); | ||||
|  | ||||
| 		lc->parent = lcparent->parent; | ||||
| 		if (lc->parent == NULL) { | ||||
| 			lc->xoff = 0; lc->yoff = 0; | ||||
| 			wp->window->layout_root = lc; | ||||
| 		} else | ||||
| 			TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); | ||||
|  | ||||
| 		layout_free_cell(lcparent); | ||||
| 	} | ||||
|  | ||||
| 	/* Fix pane offsets and sizes. */ | ||||
| 	layout_fix_offsets(wp->window->layout_root); | ||||
| 	layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								resize.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								resize.c
									
									
									
									
									
								
							| @@ -132,6 +132,7 @@ recalculate_sizes(void) | ||||
| 		log_debug( | ||||
| 		    "window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); | ||||
|  | ||||
| 		layout_resize(w, ssx, ssy); | ||||
| 		window_resize(w, ssx, ssy); | ||||
|  | ||||
| 		/* | ||||
| @@ -148,6 +149,5 @@ recalculate_sizes(void) | ||||
| 		} | ||||
|  | ||||
| 		server_redraw_window(w); | ||||
| 		layout_refresh(w, 0); | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								server.c
									
									
									
									
									
								
							| @@ -840,7 +840,9 @@ server_handle_client(struct client *c) | ||||
|  | ||||
| 	/* Ensure cursor position and mode settings. */ | ||||
| 	status = options_get_number(&c->session->options, "status"); | ||||
| 	if (wp->yoff + s->cy < c->tty.sy - status) | ||||
| 	if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) | ||||
| 		tty_cursor(&c->tty, 0, 0, 0, 0); | ||||
| 	else | ||||
| 		tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff); | ||||
|  | ||||
| 	mode = s->mode; | ||||
| @@ -1072,9 +1074,9 @@ server_check_window(struct window *w) | ||||
| 		 * pane dies). | ||||
| 		 */ | ||||
| 		if (wp->fd == -1 && !flag) { | ||||
| 			layout_close_pane(wp); | ||||
| 			window_remove_pane(w, wp); | ||||
| 			server_redraw_window(w); | ||||
| 			layout_refresh(w, 0); | ||||
| 		} else  | ||||
| 			destroyed = 0; | ||||
| 		wp = wq; | ||||
|   | ||||
							
								
								
									
										81
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								tmux.1
									
									
									
									
									
								
							| @@ -393,15 +393,17 @@ each pane takes up a certain area of the display and is a separate terminal. | ||||
| A window may be split into panes using the | ||||
| .Ic split-window | ||||
| command. | ||||
| .Pp | ||||
| Panes are numbered beginning from zero; in horizontal layouts zero is the | ||||
| leftmost pane and in vertical the topmost. | ||||
| .Pp | ||||
| Panes may be arranged using several layouts. | ||||
| The layout may be cycled with the | ||||
| .Ic next-layout | ||||
| Windows may be split horizontally (with the | ||||
| .Fl h | ||||
| flag) or vertically. | ||||
| Panes may be resized with the | ||||
| .Ic resize-pane | ||||
| command (bound to | ||||
| .Ql C-space | ||||
| .Ql C-up , | ||||
| .Ql C-down | ||||
| .Ql C-left | ||||
| and | ||||
| .Ql C-right | ||||
| by default), the current pane may be changed with the | ||||
| .Ic up-pane | ||||
| and | ||||
| @@ -410,12 +412,22 @@ commands and the | ||||
| .Ic rotate-window | ||||
| and | ||||
| .Ic swap-pane | ||||
| commands may be used to swap panes without changing the window layout. | ||||
| commands may be used to swap panes without changing their position. | ||||
| Panes are numbered beginning from zero in the order they are created. | ||||
| .Pp | ||||
| A number of preset | ||||
| .Em layouts | ||||
| are available. | ||||
| These may be selected with the | ||||
| .Ic select-layout | ||||
| command or cycled with | ||||
| .Ic next-layout | ||||
| (bound to | ||||
| .Ql C-space | ||||
| by default); once a layout is chosen, panes within it may be moved and resized as normal. | ||||
| .Pp | ||||
| The following layouts are supported: | ||||
| .Bl -tag -width Ds | ||||
| .It Ic active-only | ||||
| Only the active pane is shown \(en all other panes are hidden. | ||||
| .It Ic even-horizontal | ||||
| Panes are spread out evenly from left to right across the window. | ||||
| .It Ic even-vertical | ||||
| @@ -434,11 +446,6 @@ bottom along the right. | ||||
| See the | ||||
| .Em main-pane-width | ||||
| window option. | ||||
| .It Ic manual | ||||
| Manual layout splits windows vertically (running across); only with this layout | ||||
| may panes be resized using the | ||||
| .Ic resize-pane | ||||
| command. | ||||
| .El | ||||
| .Sh STATUS LINE | ||||
| .Nm | ||||
| @@ -980,7 +987,7 @@ Rename the current window, or the window at | ||||
| if specified, to | ||||
| .Ar new-name . | ||||
| .It Xo Ic resize-pane | ||||
| .Op Fl DU | ||||
| .Op Fl DLUR | ||||
| .Op Fl p Ar pane-index | ||||
| .Op Fl t Ar target-window | ||||
| .Op Ar adjustment | ||||
| @@ -988,11 +995,15 @@ if specified, to | ||||
| .D1 (alias: Ic resizep ) | ||||
| Resize a pane, upward with | ||||
| .Fl U | ||||
| (the default) or downward with | ||||
| .Fl D . | ||||
| (the default), downward with | ||||
| .Fl D , | ||||
| to the left with | ||||
| .Fl L | ||||
| and to the right with | ||||
| .Fl R. | ||||
| The | ||||
| .Ar adjustment | ||||
| is given in lines (the default is 1). | ||||
| is given in lines or cells (the default is 1). | ||||
| .It Xo Ic respawn-window | ||||
| .Op Fl k | ||||
| .Op Fl t Ar target-window | ||||
| @@ -1520,38 +1531,30 @@ is used. | ||||
| Execute commands from | ||||
| .Ar path . | ||||
| .It Xo Ic split-window | ||||
| .Op Fl d | ||||
| .Op Fl dhv | ||||
| .Oo Fl l | ||||
| .Ar lines | | ||||
| .Ar size | | ||||
| .Fl p Ar percentage Oc | ||||
| .Op Fl t Ar target-window | ||||
| .Op Ar command | ||||
| .Xc | ||||
| .D1 (alias: splitw ) | ||||
| Creates a new window by splitting it vertically. | ||||
| Creates a new pane by splitting the active pane: | ||||
| .Fl h | ||||
| does a horizontal split and | ||||
| .Fl v | ||||
| a vertical split; if neither is specified, | ||||
| .Fl v | ||||
| is assumed. | ||||
| The | ||||
| .Fl l | ||||
| and | ||||
| .Fl p | ||||
| options specify the size of the new window in lines, or as a percentage, | ||||
| respectively. | ||||
| options specify the size of the new window in lines (for vertical split) or in | ||||
| cells (for horizontal split), or as a percentage, respectively. | ||||
| All other options have the same meaning as in the | ||||
| .Ic new-window | ||||
| command. | ||||
| .Pp | ||||
| A few notes with regard to panes: | ||||
| .Bl -enum -compact | ||||
| .It | ||||
| If attempting to split a window with less than eight lines, an error will be | ||||
| shown. | ||||
| .It | ||||
| If the window is resized, as many panes are shown as can fit without reducing | ||||
| them below four lines. | ||||
| .It | ||||
| The minimum pane size is four lines (including the separator line). | ||||
| .It | ||||
| The panes are indexed from top (0) to bottom, with no numbers skipped. | ||||
| .El | ||||
| .It Xo Ic start-server | ||||
| .Xc | ||||
| .D1 (alias: Ic start ) | ||||
|   | ||||
							
								
								
									
										81
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -47,8 +47,11 @@ extern const char    *__progname; | ||||
| /* Default prompt history length. */ | ||||
| #define PROMPT_HISTORY 100 | ||||
|  | ||||
| /* Minimum pane size. */ | ||||
| #define PANE_MINIMUM 5	/* includes separator line */ | ||||
| /*  | ||||
|  * Minimum layout cell size, NOT including separator line. The scroll region | ||||
|  * cannot be one line in height so this must be at least two. | ||||
|  */ | ||||
| #define PANE_MINIMUM 2 | ||||
|  | ||||
| /* Automatic name refresh interval, in milliseconds. */ | ||||
| #define NAME_INTERVAL 500 | ||||
| @@ -592,6 +595,7 @@ struct window_mode { | ||||
| /* Child window structure. */ | ||||
| struct window_pane { | ||||
| 	struct window	*window; | ||||
| 	struct layout_cell *layout_cell; | ||||
|  | ||||
| 	u_int		 sx; | ||||
| 	u_int		 sy; | ||||
| @@ -635,7 +639,9 @@ struct window { | ||||
|  | ||||
| 	struct window_pane *active; | ||||
| 	struct window_panes panes; | ||||
|  | ||||
| 	u_int		 layout; | ||||
| 	struct layout_cell *layout_root; | ||||
|  | ||||
| 	u_int		 sx; | ||||
| 	u_int		 sy; | ||||
| @@ -664,6 +670,34 @@ struct winlink { | ||||
| RB_HEAD(winlinks, winlink); | ||||
| SLIST_HEAD(winlink_stack, winlink); | ||||
|  | ||||
| /* Layout direction. */ | ||||
| enum layout_type { | ||||
| 	LAYOUT_LEFTRIGHT, | ||||
| 	LAYOUT_TOPBOTTOM, | ||||
| 	LAYOUT_WINDOWPANE | ||||
| }; | ||||
|  | ||||
| /* Layout cells queue. */ | ||||
| TAILQ_HEAD(layout_cells, layout_cell); | ||||
|  | ||||
| /* Layout cell. */ | ||||
| struct layout_cell { | ||||
| 	enum layout_type type; | ||||
|  | ||||
| 	struct layout_cell *parent; | ||||
|  | ||||
| 	u_int		 sx; | ||||
| 	u_int		 sy; | ||||
|  | ||||
| 	u_int		 xoff; | ||||
| 	u_int		 yoff; | ||||
|  | ||||
| 	struct window_pane *wp; | ||||
| 	struct layout_cells cells; | ||||
|  | ||||
| 	TAILQ_ENTRY(layout_cell) entry; | ||||
| }; | ||||
|  | ||||
| /* Paste buffer. */ | ||||
| struct paste_buffer { | ||||
|      	char		*data; | ||||
| @@ -1446,8 +1480,7 @@ struct window	*window_create(const char *, const char *, | ||||
| void		 window_destroy(struct window *); | ||||
| int		 window_resize(struct window *, u_int, u_int); | ||||
| void		 window_set_active_pane(struct window *, struct window_pane *); | ||||
| struct window_pane *window_add_pane(struct window *, int, | ||||
| 		     const char *, const char *, const char **, u_int, char **); | ||||
| struct window_pane *window_add_pane(struct window *, u_int, char **); | ||||
| void		 window_remove_pane(struct window *, struct window_pane *); | ||||
| struct window_pane *window_pane_at_index(struct window *, u_int); | ||||
| u_int		 window_pane_index(struct window *, struct window_pane *); | ||||
| @@ -1467,20 +1500,38 @@ void		 window_pane_mouse(struct window_pane *, | ||||
|     		     struct client *, u_char, u_char, u_char); | ||||
| int		 window_pane_visible(struct window_pane *); | ||||
| char		*window_pane_search( | ||||
| 		     struct window_pane *, const char *, u_int *); | ||||
|     		     struct window_pane *, const char *, u_int *); | ||||
|  | ||||
| /* layout.c */ | ||||
| const char * 	 layout_name(struct window *); | ||||
| int		 layout_lookup(const char *); | ||||
| void		 layout_refresh(struct window *, int); | ||||
| int		 layout_resize(struct window_pane *, int); | ||||
| int		 layout_select(struct window *, u_int); | ||||
| void		 layout_next(struct window *); | ||||
| void		 layout_previous(struct window *); | ||||
| struct layout_cell *layout_create_cell(struct layout_cell *); | ||||
| void		 layout_free_cell(struct layout_cell *); | ||||
| void		 layout_print_cell(struct layout_cell *, const char *, u_int); | ||||
| void		 layout_set_size( | ||||
| 		     struct layout_cell *, u_int, u_int, u_int, u_int); | ||||
| void		 layout_make_leaf( | ||||
| 		     struct layout_cell *, struct window_pane *); | ||||
| void		 layout_make_node(struct layout_cell *, enum layout_type); | ||||
| void		 layout_fix_offsets(struct layout_cell *); | ||||
| void		 layout_fix_panes(struct window *, u_int, u_int); | ||||
| u_int		 layout_resize_check(struct layout_cell *, enum layout_type); | ||||
| void		 layout_resize_adjust( | ||||
| 		     struct layout_cell *, enum layout_type, int); | ||||
| void		 layout_init(struct window *); | ||||
| void		 layout_free(struct window *); | ||||
| void		 layout_resize(struct window *, u_int, u_int); | ||||
| void		 layout_resize_pane( | ||||
|     		     struct window_pane *, enum layout_type, int); | ||||
| int		 layout_split_pane(struct window_pane *, | ||||
| 		     enum layout_type, int, struct window_pane *); | ||||
| void		 layout_close_pane(struct window_pane *); | ||||
|  | ||||
| /* layout-manual.c */ | ||||
| void		 layout_manual_v_refresh(struct window *, int); | ||||
| void		 layout_manual_v_resize(struct window_pane *, int); | ||||
| /* layout-set.c */ | ||||
| const char	*layout_set_name(u_int); | ||||
| int		 layout_set_lookup(const char *); | ||||
| u_int		 layout_set_select(struct window *, u_int); | ||||
| u_int		 layout_set_next(struct window *); | ||||
| u_int		 layout_set_previous(struct window *); | ||||
| void		 layout_set_active_changed(struct window *); | ||||
|  | ||||
| /* window-clock.c */ | ||||
| extern const struct window_mode window_clock_mode; | ||||
|   | ||||
							
								
								
									
										53
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								window.c
									
									
									
									
									
								
							| @@ -35,7 +35,7 @@ | ||||
| #include "tmux.h" | ||||
|  | ||||
| /* | ||||
|  * Each window is attached to one or two panes, each of which is a pty. This | ||||
|  * Each window is attached to a number of panes, each of which is a pty. This | ||||
|  * file contains code to handle them. | ||||
|  * | ||||
|  * A pane has two buffers attached, these are filled and emptied by the main | ||||
| @@ -230,8 +230,10 @@ window_create1(u_int sx, u_int sy) | ||||
|  | ||||
| 	TAILQ_INIT(&w->panes); | ||||
| 	w->active = NULL; | ||||
| 	w->layout = 0; | ||||
|  | ||||
| 	w->layout = 0; | ||||
| 	w->layout_root = NULL; | ||||
| 	 | ||||
| 	w->sx = sx; | ||||
| 	w->sy = sy; | ||||
|  | ||||
| @@ -254,15 +256,20 @@ struct window * | ||||
| window_create(const char *name, const char *cmd, const char *cwd, | ||||
|     const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause) | ||||
| { | ||||
| 	struct window	*w; | ||||
| 	struct window		*w; | ||||
| 	struct window_pane	*wp; | ||||
|  | ||||
| 	w = window_create1(sx, sy); | ||||
| 	if (window_add_pane(w, -1, cmd, cwd, envp, hlimit, cause) == NULL) { | ||||
| 	if ((wp = window_add_pane(w, hlimit, cause)) == NULL) { | ||||
| 		window_destroy(w); | ||||
| 		return (NULL); | ||||
| 	} | ||||
| 	layout_init(w); | ||||
| 	if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) { | ||||
| 		window_destroy(w); | ||||
| 		return (NULL); | ||||
| 	} | ||||
| 	w->active = TAILQ_FIRST(&w->panes); | ||||
|  | ||||
| 	if (name != NULL) { | ||||
| 		w->name = xstrdup(name); | ||||
| 		options_set_number(&w->options, "automatic-rename", 0); | ||||
| @@ -282,6 +289,9 @@ window_destroy(struct window *w) | ||||
| 	while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) | ||||
| 		ARRAY_TRUNC(&windows, 1); | ||||
|  | ||||
| 	if (w->layout_root != NULL) | ||||
| 		layout_free(w); | ||||
|  | ||||
| 	options_free(&w->options); | ||||
|  | ||||
| 	window_destroy_panes(w); | ||||
| @@ -304,7 +314,6 @@ void | ||||
| window_set_active_pane(struct window *w, struct window_pane *wp) | ||||
| { | ||||
| 	w->active = wp; | ||||
|  | ||||
| 	while (!window_pane_visible(w->active)) { | ||||
| 		w->active = TAILQ_PREV(w->active, window_panes, entry); | ||||
| 		if (w->active == NULL) | ||||
| @@ -315,41 +324,15 @@ window_set_active_pane(struct window *w, struct window_pane *wp) | ||||
| } | ||||
|  | ||||
| struct window_pane * | ||||
| window_add_pane(struct window *w, int wanty, const char *cmd, | ||||
|     const char *cwd, const char **envp, u_int hlimit, char **cause) | ||||
| window_add_pane(struct window *w, u_int hlimit, unused char **cause) | ||||
| { | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 sizey; | ||||
|  | ||||
| 	if (TAILQ_EMPTY(&w->panes)) | ||||
| 		wanty = w->sy; | ||||
| 	else { | ||||
| 		sizey = w->active->sy - 1; /* for separator */ | ||||
| 		if (sizey < PANE_MINIMUM * 2) { | ||||
| 			*cause = xstrdup("pane too small"); | ||||
| 			return (NULL); | ||||
| 		} | ||||
|  | ||||
|   		if (wanty == -1) | ||||
| 			wanty = sizey / 2; | ||||
|  | ||||
| 		if (wanty < PANE_MINIMUM) | ||||
| 			wanty = PANE_MINIMUM; | ||||
| 		if ((u_int) wanty > sizey - PANE_MINIMUM) | ||||
| 			wanty = sizey - PANE_MINIMUM; | ||||
|  | ||||
| 		window_pane_resize(w->active, w->sx, sizey - wanty); | ||||
| 	} | ||||
|  | ||||
| 	wp = window_pane_create(w, w->sx, wanty, hlimit); | ||||
| 	wp = window_pane_create(w, w->sx, w->sy, hlimit); | ||||
| 	if (TAILQ_EMPTY(&w->panes)) | ||||
| 		TAILQ_INSERT_HEAD(&w->panes, wp, entry); | ||||
| 	else | ||||
| 		TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); | ||||
| 	if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) { | ||||
| 		window_remove_pane(w, wp); | ||||
| 		return (NULL); | ||||
| 	} | ||||
| 	return (wp); | ||||
| } | ||||
|  | ||||
| @@ -435,6 +418,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) | ||||
|  | ||||
| 	wp->mode = NULL; | ||||
|  | ||||
| 	wp->layout_cell = NULL; | ||||
|  | ||||
| 	wp->xoff = 0; | ||||
|  	wp->yoff = 0; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott