xref: /openbsd-src/usr.bin/tmux/cmd-split-window.c (revision b0ba120264cff76725751bc2a1154a04281c53ea)
1*b0ba1202Snicm /* $OpenBSD: cmd-split-window.c,v 1.115 2025/01/27 09:05:22 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
21e1803d63Snicm #include <errno.h>
22e1803d63Snicm #include <fcntl.h>
233a5ec08bSnicm #include <paths.h>
24311827fbSnicm #include <stdlib.h>
25e1803d63Snicm #include <string.h>
26311827fbSnicm #include <unistd.h>
27311827fbSnicm 
28311827fbSnicm #include "tmux.h"
29311827fbSnicm 
30311827fbSnicm /*
31311827fbSnicm  * Split a window (add a new pane).
32311827fbSnicm  */
33311827fbSnicm 
341905ff33Snicm #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
351905ff33Snicm 
3668e0a7f2Snicm static enum cmd_retval	cmd_split_window_exec(struct cmd *,
3768e0a7f2Snicm 			    struct cmdq_item *);
38311827fbSnicm 
39311827fbSnicm const struct cmd_entry cmd_split_window_entry = {
40c057646bSnicm 	.name = "split-window",
41c057646bSnicm 	.alias = "splitw",
42c057646bSnicm 
43a51dead1Snicm 	.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
44baddd6b2Snicm 	.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
45d8b32369Snicm 		 "[-F format] [-l size] " CMD_TARGET_PANE_USAGE
46d8b32369Snicm 		 " [shell-command]",
47c057646bSnicm 
48bf0d297eSnicm 	.target = { 't', CMD_FIND_PANE, 0 },
498d471e80Snicm 
508d471e80Snicm 	.flags = 0,
51c057646bSnicm 	.exec = cmd_split_window_exec
52311827fbSnicm };
53311827fbSnicm 
54dc1f0f5fSnicm static enum cmd_retval
5568e0a7f2Snicm cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
56311827fbSnicm {
5790d7ba38Snicm 	struct args		*args = cmd_get_args(self);
58823b6d6dSnicm 	struct cmd_find_state	*current = cmdq_get_current(item);
59040343aeSnicm 	struct cmd_find_state	*target = cmdq_get_target(item);
601693b10bSnicm 	struct spawn_context	 sc = { 0 };
61035dc73dSnicm 	struct client		*tc = cmdq_get_target_client(item);
62040343aeSnicm 	struct session		*s = target->s;
63040343aeSnicm 	struct winlink		*wl = target->wl;
6479fb7095Snicm 	struct window		*w = wl->window;
65040343aeSnicm 	struct window_pane	*wp = target->wp, *new_wp;
66af9e4c5dSnicm 	enum layout_type	 type;
67572cd943Snicm 	struct layout_cell	*lc;
6845436ca5Snicm 	struct cmd_find_state	 fs;
69113211eaSnicm 	int			 size, flags, input;
70113211eaSnicm 	const char		*template;
71113211eaSnicm 	char			*cause = NULL, *cp;
7205b80794Snicm 	struct args_value	*av;
73113211eaSnicm 	u_int			 count = args_count(args), curval = 0;
74311827fbSnicm 
75113211eaSnicm 	type = LAYOUT_TOPBOTTOM;
76ca7befccSnicm 	if (args_has(args, 'h'))
77af9e4c5dSnicm 		type = LAYOUT_LEFTRIGHT;
78113211eaSnicm 
79113211eaSnicm 	/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
80113211eaSnicm 	if (args_has(args, 'l') || args_has(args, 'p')) {
8179fb7095Snicm 		if (args_has(args, 'f')) {
8279fb7095Snicm 			if (type == LAYOUT_TOPBOTTOM)
83113211eaSnicm 				curval = w->sy;
8479fb7095Snicm 			else
85113211eaSnicm 				curval = w->sx;
8679fb7095Snicm 		} else {
8728988ef6Snicm 			if (type == LAYOUT_TOPBOTTOM)
88113211eaSnicm 				curval = wp->sy;
8928988ef6Snicm 			else
90113211eaSnicm 				curval = wp->sx;
91ca7befccSnicm 		}
9228988ef6Snicm 	}
93113211eaSnicm 
94c26c4f79Snicm 	size = -1;
95113211eaSnicm 	if (args_has(args, 'l')) {
96113211eaSnicm 		size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
97113211eaSnicm 		    item, &cause);
98113211eaSnicm 	} else if (args_has(args, 'p')) {
99e9310faaSnicm 		size = args_strtonum_and_expand(args, 'p', 0, 100, item,
100113211eaSnicm 		    &cause);
101113211eaSnicm 		if (cause == NULL)
102113211eaSnicm 			size = curval * size / 100;
103113211eaSnicm 	}
104113211eaSnicm 	if (cause != NULL) {
105113211eaSnicm 		cmdq_error(item, "size %s", cause);
106113211eaSnicm 		free(cause);
107113211eaSnicm 		return (CMD_RETURN_ERROR);
108113211eaSnicm 	}
109c26c4f79Snicm 
110baddd6b2Snicm 	window_push_zoom(wp->window, 1, args_has(args, 'Z'));
1111693b10bSnicm 	input = (args_has(args, 'I') && count == 0);
112c26c4f79Snicm 
113c26c4f79Snicm 	flags = 0;
114c26c4f79Snicm 	if (args_has(args, 'b'))
115c26c4f79Snicm 		flags |= SPAWN_BEFORE;
116c26c4f79Snicm 	if (args_has(args, 'f'))
117c26c4f79Snicm 		flags |= SPAWN_FULLSIZE;
1181693b10bSnicm 	if (input || (count == 1 && *args_string(args, 0) == '\0'))
119aab3c1a6Snicm 		flags |= SPAWN_EMPTY;
120c26c4f79Snicm 
121c26c4f79Snicm 	lc = layout_split_pane(wp, type, size, flags);
122c26c4f79Snicm 	if (lc == NULL) {
123c26c4f79Snicm 		cmdq_error(item, "no space for new pane");
124c26c4f79Snicm 		return (CMD_RETURN_ERROR);
125c26c4f79Snicm 	}
126c26c4f79Snicm 
127c26c4f79Snicm 	sc.item = item;
128c26c4f79Snicm 	sc.s = s;
129c26c4f79Snicm 	sc.wl = wl;
130c26c4f79Snicm 
131c26c4f79Snicm 	sc.wp0 = wp;
132c26c4f79Snicm 	sc.lc = lc;
133c26c4f79Snicm 
134d8b32369Snicm 	args_to_vector(args, &sc.argc, &sc.argv);
135d0772b58Snicm 	sc.environ = environ_create();
136d0772b58Snicm 
13705b80794Snicm 	av = args_first_value(args, 'e');
13805b80794Snicm 	while (av != NULL) {
139825f884aSnicm 		environ_put(sc.environ, av->string, 0);
14005b80794Snicm 		av = args_next_value(av);
141d0772b58Snicm 	}
142c26c4f79Snicm 
143c26c4f79Snicm 	sc.idx = -1;
144c26c4f79Snicm 	sc.cwd = args_get(args, 'c');
145c26c4f79Snicm 
146c26c4f79Snicm 	sc.flags = flags;
147c26c4f79Snicm 	if (args_has(args, 'd'))
148c26c4f79Snicm 		sc.flags |= SPAWN_DETACHED;
149baddd6b2Snicm 	if (args_has(args, 'Z'))
150baddd6b2Snicm 		sc.flags |= SPAWN_ZOOM;
151c26c4f79Snicm 
152c26c4f79Snicm 	if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
153c26c4f79Snicm 		cmdq_error(item, "create pane failed: %s", cause);
154c26c4f79Snicm 		free(cause);
1551693b10bSnicm 		if (sc.argv != NULL)
1561693b10bSnicm 			cmd_free_argv(sc.argc, sc.argv);
15705b80794Snicm 		environ_free(sc.environ);
158c26c4f79Snicm 		return (CMD_RETURN_ERROR);
159c26c4f79Snicm 	}
160d110efb0Snicm 	if (input) {
161d110efb0Snicm 		switch (window_pane_start_input(new_wp, item, &cause)) {
162d110efb0Snicm 		case -1:
163249e1654Snicm 			server_client_remove_pane(new_wp);
164aab3c1a6Snicm 			layout_close_pane(new_wp);
165aab3c1a6Snicm 			window_remove_pane(wp->window, new_wp);
166aab3c1a6Snicm 			cmdq_error(item, "%s", cause);
167aab3c1a6Snicm 			free(cause);
1681693b10bSnicm 			if (sc.argv != NULL)
1691693b10bSnicm 				cmd_free_argv(sc.argc, sc.argv);
17005b80794Snicm 			environ_free(sc.environ);
171aab3c1a6Snicm 			return (CMD_RETURN_ERROR);
172d110efb0Snicm 		case 1:
173d110efb0Snicm 			input = 0;
174d110efb0Snicm 			break;
175d110efb0Snicm 		}
176aab3c1a6Snicm 	}
177c26c4f79Snicm 	if (!args_has(args, 'd'))
178c26c4f79Snicm 		cmd_find_from_winlink_pane(current, wl, new_wp, 0);
179baddd6b2Snicm 	window_pop_zoom(wp->window);
180c26c4f79Snicm 	server_redraw_window(wp->window);
181311827fbSnicm 	server_status_session(s);
182311827fbSnicm 
183ca7befccSnicm 	if (args_has(args, 'P')) {
184cc9e5b00Snicm 		if ((template = args_get(args, 'F')) == NULL)
18561e1d212Snicm 			template = SPLIT_WINDOW_TEMPLATE;
186035dc73dSnicm 		cp = format_single(item, template, tc, s, wl, new_wp);
18768e0a7f2Snicm 		cmdq_print(item, "%s", cp);
1887d053cf9Snicm 		free(cp);
1892c3a3119Snicm 	}
190e1803d63Snicm 
1910772530eSnicm 	cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
192844b9093Snicm 	cmdq_insert_hook(s, item, &fs, "after-split-window");
193765b9a58Snicm 
1941693b10bSnicm 	if (sc.argv != NULL)
1951693b10bSnicm 		cmd_free_argv(sc.argc, sc.argv);
196d0772b58Snicm 	environ_free(sc.environ);
197aab3c1a6Snicm 	if (input)
198aab3c1a6Snicm 		return (CMD_RETURN_WAIT);
199a224d0d3Snicm 	return (CMD_RETURN_NORMAL);
200311827fbSnicm }
201