Merge pull request #4979 from daneofmanythings/floating_panes--new-pane

refactor: Combined the code for `split-window` and `new-pane`
This commit is contained in:
Nicholas Marriott
2026-04-22 10:29:07 +03:00
committed by GitHub
4 changed files with 318 additions and 405 deletions

View File

@@ -115,7 +115,6 @@ dist_tmux_SOURCES = \
cmd-lock-server.c \
cmd-minimise-pane.c \
cmd-move-window.c \
cmd-new-pane.c \
cmd-new-session.c \
cmd-new-window.c \
cmd-parse.y \

View File

@@ -1,280 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* 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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
#define NEW_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_new_pane_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "bc:de:fF:h:Iklm:p:PR:s:S:t:w:x:y:Z", 0, -1, NULL },
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] "
"[-R inactive-border-style] [-s style] [-S active-border-style] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_new_pane_exec
};
static enum cmd_retval
cmd_new_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp;
struct layout_cell *lc;
struct cmd_find_state fs;
int flags, input;
const char *template, *style;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args);
int x, y;
u_int sx, sy, pct;
static int last_x = 0, last_y = 0;
if (args_has(args, 'f')) {
sx = w->sx;
sy = w->sy;
} else {
if (args_has(args, 'l')) {
sx = args_percentage_and_expand(args, 'l', 0, INT_MAX, w->sx,
item, &cause);
sy = args_percentage_and_expand(args, 'l', 0, INT_MAX, w->sy,
item, &cause);
} else if (args_has(args, 'p')) {
pct = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL) {
sx = w->sx * pct / 100;
sy = w->sy * pct / 100;
}
} else if (cause == NULL) {
sx = w->sx / 2;
sy = w->sy / 2;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'w')) {
sx = args_strtonum_and_expand(args, 'w', 1, USHRT_MAX, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'h')) {
sy = args_strtonum_and_expand(args, 'h', 1, USHRT_MAX, item,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'x')) {
x = args_strtonum_and_expand(args, 'x', SHRT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (last_x == 0) {
x = 5;
} else {
x = (last_x += 5);
if (last_x > (int)w->sx)
x = 5;
}
}
if (args_has(args, 'y')) {
y = args_strtonum_and_expand(args, 'y', SHRT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (last_y == 0) {
y = 5;
} else {
y = (last_y += 5);
if (last_y > (int)w->sy)
y = 5;
}
}
input = (args_has(args, 'I') && count == 0);
flags = SPAWN_FLOATING;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (input || (count == 1 && *args_string(args, 0) == '\0'))
flags |= SPAWN_EMPTY;
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
/* Floating panes sit in layout cells which are not in the layout_root
* tree so we call it with parent == NULL.
*/
lc = layout_create_cell(NULL);
lc->xoff = x;
lc->yoff = y;
lc->sx = sx;
lc->sy = sy;
sc.lc = lc;
last_x = x; /* Statically save last xoff & yoff so that new */
last_y = y; /* floating panes offset so they don't overlap. */
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
style = args_get(args, 's');
if (style != NULL) {
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
}
style = args_get(args, 'S');
if (style != NULL) {
if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style);
return (CMD_RETURN_ERROR);
}
}
style = args_get(args, 'R');
if (style != NULL) {
if (options_set_string(new_wp->options, "pane-border-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s", style);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm'))
options_set_string(new_wp->options, "remain-on-exit-format",
0, "%s", args_get(args, 'm'));
}
if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) {
case -1:
server_client_remove_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
case 1:
input = 0;
break;
}
}
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_PANE_TEMPLATE;
cp = format_single(item, template, tc, s, wl, new_wp);
cmdq_print(item, "%s", cp);
free(cp);
}
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
cmdq_insert_hook(s, item, &fs, "after-split-window");
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);
return (CMD_RETURN_NORMAL);
}

View File

@@ -27,22 +27,24 @@
#include "tmux.h"
/*
* Split a window (add a new pane).
* Create a new pane.
*/
#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_split_window_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE
" [shell-command [argument ...]]",
.args = { "bc:de:fF:hIkl:m:M:p:PR:s:S:t:x:X:y:Y:vZ", 0, -1, NULL },
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-M mode] "
"[-R inactive-border-style] [-s style] "
"[-S active-border-style] [-x width] [-X x-position]"
"[-y length] [-Y y-position]" CMD_TARGET_PANE_USAGE
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -50,30 +52,122 @@ const struct cmd_entry cmd_split_window_entry = {
.exec = cmd_split_window_exec
};
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bc:de:fF:hIkl:m:M:p:PR:s:S:t:x:X:y:Y:vZ", 0, -1, NULL },
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-M mode] "
"[-R inactive-border-style] [-s style] "
"[-S active-border-style] [-x width] [-X x-position]"
"[-y length] [-Y y-position]" CMD_TARGET_PANE_USAGE
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_split_window_exec
};
enum new_pane_mode {
FLOATING,
TILED,
NONE,
};
static struct layout_cell *
cmd_split_window_get_floating_layout_cell(struct cmdq_item *item,
struct args *args, struct window *w)
{
struct layout_cell *lc = NULL;
char *cause = NULL;
int x, y;
u_int sx, sy;
static int last_x = 0, last_y = 0;
/* Default size. */
sx = w->sx / 2;
sy = w->sy / 2;
if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, USHRT_MAX, w->sx,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (NULL);
}
}
if (args_has(args, 'y')) {
sy = args_percentage_and_expand(args, 'y', 0, USHRT_MAX, w->sy,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (NULL);
}
}
/* If a position is not defined, it defaults to cascading. */
if (args_has(args, 'X')) {
x = args_percentage_and_expand(args, 'X', 0, USHRT_MAX, w->sx,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (NULL);
}
} else if (last_x == 0)
x = 4;
else {
x = (last_x += 4);
if (last_x > (int)w->sx)
x = 4;
}
if (args_has(args, 'Y')) {
y = args_percentage_and_expand(args, 'Y', 0, USHRT_MAX, w->sy,
item, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (NULL);
}
} else if (last_y == 0)
y = 2;
else {
y = (last_y += 2);
if (last_y > (int)w->sy)
y = 2;
}
/* Floating panes sit in layout cells which are not in the layout_root
* tree so we call it with parent == NULL.
*/
lc = layout_create_cell(NULL);
lc->xoff = x;
lc->yoff = y;
lc->sx = sx;
lc->sy = sy;
last_x = x; /* Statically save last xoff & yoff so that new */
last_y = y; /* floating panes offset so they don't overlap. */
return (lc);
}
static struct layout_cell *
cmd_split_window_get_tiled_layout_cell(struct cmdq_item *item,
struct args *args, struct window *w, struct window_pane *wp, int flags)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
struct cmd_find_state fs;
int size, flags, input;
const char *template;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args), curval = 0;
struct layout_cell *lc = NULL;
char *cause = NULL;
int size;
u_int curval = 0;
if (wp->flags & PANE_FLOATING) {
cmdq_error(item, "can't split a floating pane");
return (CMD_RETURN_ERROR);
return (NULL);
}
type = LAYOUT_TOPBOTTOM;
@@ -108,13 +202,55 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
return (NULL);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL)
cmdq_error(item, "no space for new pane");
return (lc);
}
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp;
struct layout_cell *lc = NULL;
struct cmd_find_state fs;
int flags, input;
const char *template, *style;
char *cause = NULL, *cp;
struct args_value *av;
u_int count = args_count(args);
enum new_pane_mode pane_mode = NONE;
if (args_has(args, 'M')) {
if (strcasecmp(args_get(args, 'M'), "f") == 0)
pane_mode = FLOATING;
else if (strcasecmp(args_get(args, 'M'), "t") == 0)
pane_mode = TILED;
else
pane_mode = NONE;
} else {
if (cmd_get_entry(self) == &cmd_new_pane_entry)
pane_mode = FLOATING;
else
pane_mode = TILED;
}
input = (args_has(args, 'I') && count == 0);
flags = 0;
flags = pane_mode == FLOATING ? SPAWN_FLOATING : 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
@@ -122,11 +258,19 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (input || (count == 1 && *args_string(args, 0) == '\0'))
flags |= SPAWN_EMPTY;
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "no space for new pane");
if (pane_mode == FLOATING)
lc = cmd_split_window_get_floating_layout_cell(item, args, w);
else if (pane_mode == TILED)
lc = cmd_split_window_get_tiled_layout_cell(item, args, w, wp,
flags);
else {
cmdq_error(item, "unrecognized pane mode '%s'",
args_get(args, 'M'));
return (CMD_RETURN_ERROR);
}
if (lc == NULL)
return (CMD_RETURN_ERROR);
sc.item = item;
sc.s = s;
@@ -161,11 +305,48 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
style = args_get(args, 's');
if (style != NULL) {
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
}
style = args_get(args, 'S');
if (style != NULL) {
if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style);
return (CMD_RETURN_ERROR);
}
}
style = args_get(args, 'R');
if (style != NULL) {
if (options_set_string(new_wp->options, "pane-border-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s", style);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm'))
options_set_string(new_wp->options,
"remain-on-exit-format",
0, "%s", args_get(args, 'm'));
}
if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) {
case -1:
server_client_remove_pane(new_wp);
layout_close_pane(new_wp);
if (pane_mode == TILED)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);

191
tmux.1
View File

@@ -3323,53 +3323,63 @@ but a different format may be specified with
.Fl F .
.Tg newp
.It Xo Ic new-pane
.Op Fl bdefIPZ
.Op Fl bdefhIkPvZ
.Op Fl c Ar start-directory
.Op Fl e Ar environment
.Op Fl F Ar format
.Op Fl h Ar height
.Op Fl k
.Op Fl l Ar size
.Op Fl m Ar message
.Op Fl M Ar mode
.Op Fl p Ar percentage
.Op Fl R Ar inactive-border-style
.Op Fl s Ar style
.Op Fl S Ar active-border-style
.Op Fl t Ar target-pane
.Op Fl w Ar width
.Op Fl x Ar x-position
.Op Fl y Ar y-position
.Op Fl x Ar width
.Op Fl X Ar x-position
.Op Fl y Ar length
.Op Fl Y Ar y-position
.Op Ar shell-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic newp
Create a new floating pane.
The
.Fl w ,
.Fl h ,
.Fl x ,
and
.Fl y
options set the width, height, and position of the pane; if not given,
the pane is sized to half the window dimensions and offset from the
previous floating pane.
The
.Fl l
and
.Fl p
options set the size in lines or as a percentage.
The
.Fl f
option uses the full window dimensions.
Create a new pane.
A
.Ar mode
may be specified with the
.Fl M
option and must be followed by one of the following special values:
.Bl -column "XXXXX" -offset indent
.It Sy "Value" Ta Sy "Meaning"
.It Li "f" Ta "floating pane above the current layout"
.It Li "t" Ta "tiled into the layout by splitting a pane"
.El
.Pp
If no
.Ar mode
is specified,
.Ic f
is assumed.
When creating a tiled pane, a target pane may be specified with
.Fl t .
Note that some options will only affect one
.Ar mode .
.Pp
If
.Fl d
is given, the session does not make the new pane the current pane.
.Fl Z
zooms if the window is not zoomed.
.Pp
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
.Fl k
keeps the pane open after the shell command exits and waits for a
keypress (any non-mouse key) before closing it.
keeps the pane open after the optional
.Ar shell-command
exits and waits for a keypress (any non-mouse key) before closing it.
The message shown is controlled by the
.Ic remain-on-exit-format
option.
@@ -3380,7 +3390,6 @@ but also sets the
.Ic remain-on-exit-format
option for this pane to
.Ar message .
.Pp
An empty
.Ar shell-command
(\[aq]\[aq]) will create a pane with no command running in it.
@@ -3390,14 +3399,50 @@ flag (if
.Ar shell-command
is not specified or empty)
will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux splitw \-dI &
.Ed
.Pp
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
For floating panes, the following options are availible:
The
.Fl x ,
.Fl y ,
.Fl X ,
and
.Fl Y
options set the width, height, and position of the pane.
If not given, the pane is sized to half the window dimensions and offset from
the previous floating pane.
These four options may be followed by '%' to specify a percentage of the
current window dimensions.
.Pp
For tiled panes, the following options are availible:
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
All other options have the same meaning as for the
.Ic new-window
@@ -3726,65 +3771,33 @@ the command behaves like
.Ic last\-window .
.Tg splitw
.It Xo Ic split\-window
.Op Fl bdfhIvPZ
.Op Fl c Ar start\-directory
.Op Fl bdefhIkPvZ
.Op Fl c Ar start-directory
.Op Fl e Ar environment
.Op Fl F Ar format
.Op Fl H Ar height
.Op Fl l Ar size
.Op Fl t Ar target\-pane
.Op Ar shell\-command Op Ar argument ...
.Op Fl m Ar message
.Op Fl M Ar mode
.Op Fl p Ar percentage
.Op Fl R Ar inactive-border-style
.Op Fl s Ar style
.Op Fl S Ar active-border-style
.Op Fl t Ar target-pane
.Op Fl w Ar width
.Op Fl x Ar x-position
.Op Fl y Ar y-position
.Op Ar shell-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic splitw
Create a new pane by splitting
.Ar target\-pane :
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Fl Z
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
Creates a new pane.
Default behavior is to split the pane in a tiled layout.
Shares behavior with
.Ic new-pane .
.Pp
An empty
.Ar shell\-command
(\[aq]\[aq]) will create a pane with no command running in it.
Output can be sent to such a pane with the
.Ic display\-message
command.
The
.Fl I
flag (if
.Ar shell\-command
is not specified or empty)
will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux splitw \-dI &
.Ed
.Pp
All other options have the same meaning as for the
.Ic new\-window
command.
See
.Ic new-pane
for more details.
.Tg swapp
.It Xo Ic swap\-pane
.Op Fl dDUZ